summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TEST_MAPPING8
-rw-r--r--apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java4
-rw-r--r--apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java220
-rw-r--r--apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java107
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java59
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java1197
-rw-r--r--apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java169
-rw-r--r--apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java132
-rw-r--r--apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java93
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java1818
-rwxr-xr-xapct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py121
-rw-r--r--apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java182
-rw-r--r--apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java72
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java79
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java124
-rw-r--r--apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java67
-rw-r--r--apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java47
-rw-r--r--apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java80
-rw-r--r--apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java137
-rw-r--r--apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java58
-rw-r--r--apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java136
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java99
-rw-r--r--apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java110
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java16
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl1
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java42
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java16
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java90
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java16
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java38
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java98
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/README.md2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Scribe.java38
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java24
-rw-r--r--cmds/bootanimation/BootAnimation.cpp23
-rw-r--r--cmds/bootanimation/BootAnimation.h2
-rw-r--r--core/api/current.txt18
-rw-r--r--core/api/system-current.txt5
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityInputMethodSession.java33
-rw-r--r--core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java116
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java133
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl17
-rw-r--r--core/java/android/accessibilityservice/InputMethod.java175
-rw-r--r--core/java/android/app/ActivityClient.java8
-rw-r--r--core/java/android/app/ActivityThread.java8
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/UiAutomation.java26
-rw-r--r--core/java/android/app/trust/TEST_MAPPING13
-rw-r--r--core/java/android/content/AbstractThreadedSyncAdapter.java17
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/content/OWNERS3
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl8
-rw-r--r--core/java/android/content/pm/SigningDetails.java143
-rw-r--r--core/java/android/content/res/Configuration.java16
-rw-r--r--core/java/android/hardware/biometrics/SensorLocationInternal.java9
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java51
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionSession.java15
-rw-r--r--core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl3
-rw-r--r--core/java/android/hardware/camera2/extension/ICaptureCallback.aidl2
-rw-r--r--core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl5
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java75
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java1
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java116
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodServiceInternal.java5
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java9
-rw-r--r--core/java/android/net/NetworkPolicy.java3
-rw-r--r--core/java/android/net/vcn/VcnManager.java8
-rw-r--r--core/java/android/service/autofill/FillResponse.java8
-rw-r--r--core/java/android/service/dreams/DreamOverlayService.java20
-rw-r--r--core/java/android/service/dreams/DreamService.java19
-rw-r--r--core/java/android/util/DisplayUtils.java15
-rw-r--r--core/java/android/util/RotationUtils.java38
-rw-r--r--core/java/android/view/CutoutSpecification.java99
-rw-r--r--core/java/android/view/DisplayCutout.java116
-rw-r--r--core/java/android/view/MotionEvent.java156
-rw-r--r--core/java/android/view/RoundedCorners.java28
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java8
-rw-r--r--core/java/android/view/View.java15
-rw-r--r--core/java/android/view/ViewRootImpl.java11
-rw-r--r--core/java/android/view/autofill/AutofillManager.java61
-rw-r--r--core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java84
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java42
-rw-r--r--core/java/android/widget/RemoteViews.java17
-rw-r--r--core/java/android/widget/TextView.java12
-rw-r--r--core/java/android/window/WindowContainerTransaction.java4
-rw-r--r--core/java/android/window/WindowProviderService.java5
-rw-r--r--core/java/android/window/WindowTokenClient.java8
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java77
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java14
-rw-r--r--core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java73
-rw-r--r--core/java/com/android/internal/app/ChooserUtil.java57
-rw-r--r--core/java/com/android/internal/app/SimpleIconFactory.java6
-rw-r--r--core/java/com/android/internal/app/chooser/SelectableTargetInfo.java5
-rw-r--r--core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl44
-rw-r--r--core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl (renamed from core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl)12
-rw-r--r--core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl51
-rw-r--r--core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java231
-rw-r--r--core/java/com/android/internal/inputmethod/InputBindResult.java14
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java210
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java180
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java20
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl4
-rw-r--r--core/java/com/android/internal/widget/LocalImageResolver.java5
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp19
-rw-r--r--core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp2
-rw-r--r--core/proto/android/service/usb.proto37
-rw-r--r--core/res/res/drawable/chooser_pinned_background.xml3
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/content/pm/SigningDetailsTest.java166
-rw-r--r--core/tests/coretests/src/android/view/DisplayCutoutTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java11
-rw-r--r--graphics/TEST_MAPPING7
-rw-r--r--keystore/java/android/security/GenerateRkpKey.java36
-rw-r--r--keystore/java/android/security/IGenerateRkpKeyService.aidl28
-rw-r--r--keystore/java/android/security/KeyStore2.java6
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java68
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java93
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java45
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java97
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java27
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java82
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml24
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu_action.xml8
-rw-r--r--libs/WindowManager/Shell/res/layout/split_decor.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt14
-rw-r--r--media/Android.bp7
-rw-r--r--media/java/android/media/MediaCodecInfo.java1
-rw-r--r--media/java/android/media/MediaFormat.java2
-rw-r--r--native/android/performance_hint.cpp12
-rw-r--r--omapi/aidl/Android.bp7
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/.hash1
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementChannel.aidl46
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementListener.aidl40
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementReader.aidl44
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementService.aidl45
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementSession.aidl48
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java165
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java136
-rw-r--r--packages/SystemUI/OWNERS1
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml8
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_preview.xml28
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml2
-rw-r--r--packages/SystemUI/res/layout/media_long_press_menu.xml105
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml84
-rw-r--r--packages/SystemUI/res/layout/media_smartspace_recommendations.xml94
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml14
-rw-r--r--packages/SystemUI/res/values/strings.xml33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/AndroidInternalsModule.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Application.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java178
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewLogger.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java181
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java222
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt224
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt6
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java105
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java72
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java60
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java3
-rw-r--r--services/core/java/com/android/server/AccessibilityManagerInternal.java21
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/AppBatteryTracker.java28
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java14
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java88
-rw-r--r--services/core/java/com/android/server/am/BaseAppStateTracker.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java39
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java8
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java40
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java187
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java11
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java146
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java44
-rw-r--r--services/core/java/com/android/server/pm/Installer.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java24
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java14
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java6
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java2
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java17
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java33
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java22
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java10
-rw-r--r--services/core/java/com/android/server/trust/TEST_MAPPING13
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java86
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java117
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java14
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java30
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java9
-rw-r--r--services/core/java/com/android/server/vcn/util/MtuUtils.java27
-rw-r--r--services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java180
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java21
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java41
-rw-r--r--services/core/java/com/android/server/wm/Task.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowContextListenerController.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java4
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java6
-rw-r--r--services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java23
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java6
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java53
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java32
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java8
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java49
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java31
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java10
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java6
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java70
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt48
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt37
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt4
-rw-r--r--tests/TrustTests/TEST_MAPPING13
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java57
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java15
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java17
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java5
-rw-r--r--tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java91
-rw-r--r--tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java82
345 files changed, 12980 insertions, 2888 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b43eb7e12170..e178583bca78 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -17,6 +17,14 @@
],
"presubmit": [
{
+ "name": "ManagedProvisioningTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
"file_patterns": [
"ApexManager\\.java",
"SystemServer\\.java",
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 665e986b91e5..3781b6dcf162 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -95,9 +95,9 @@ public class BlobStorePerfTests {
}
@After
- public void tearDown() {
+ public void tearDown() throws Exception {
mContext.getFilesDir().delete();
- runShellCommand("cmd package clear " + mContext.getPackageName());
+ mBlobStoreManager.releaseAllLeases();
runShellCommand("cmd blob_store idle-maintenance");
}
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
new file mode 100644
index 000000000000..e0c12dd660e2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+
+/**
+ * Tries to measure important BigInteger operations across a variety of BigInteger sizes. Note that
+ * BigInteger implementations commonly need to use wildly different algorithms for different sizes,
+ * so relative performance may change substantially depending on the size of the integer. This is
+ * not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BigIntegerPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ // A simple sum of products computation, mostly so we can check timing in the
+ // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
+ // repeating the multiplication, but not addition of 1, each time through the loop.
+ // Check the last few bits of the result as we go. Assumes n < 2^30.
+ // Note that we're actually squaring values in computing the product.
+ // That affects the algorithm used by some implementations.
+ private static void inner(int n, int prec) {
+ BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE);
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = 0; i < n; ++i) {
+ sum = sum.add(big.multiply(big));
+ }
+ if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) {
+ throw new AssertionError(
+ "inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) + " instead of " + n);
+ }
+ }
+
+ // Execute the above rep times, optionally timing it.
+ @Test
+ public void repeatInner() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ inner(100, i);
+ }
+ }
+ }
+
+ // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m
+ // goes from 1 to n) to about prec digits. The result has an implicit decimal point
+ // prec digits from the right.
+ private static BigInteger harmonic1000(int prec) {
+ BigInteger scaledOne = BigInteger.TEN.pow(prec);
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = 1; i <= 1000; ++i) {
+ sum = sum.add(scaledOne.divide(BigInteger.valueOf(i)));
+ }
+ return sum;
+ }
+
+ // Execute the above rep times, optionally timing it.
+ // Check results for equality, and print one, to compaare against reference.
+ @Test
+ public void repeatHarmonic1000() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 5_000; i *= 10) {
+ BigInteger refRes = harmonic1000(i);
+ BigInteger newRes = harmonic1000(i);
+ if (!newRes.equals(refRes)) {
+ throw new AssertionError(newRes + " != " + refRes);
+ }
+ if (i >= 50
+ && !refRes.toString()
+ .startsWith("748547086055034491265651820433390017652167916970")) {
+ throw new AssertionError("harmanic(" + i + ") incorrectly produced " + refRes);
+ }
+ }
+ }
+ }
+
+ // Repeatedly execute just the base conversion from the last test, allowing
+ // us to time and check it for consistency as well.
+ @Test
+ public void repeatToString() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 5_000; i *= 10) {
+ BigInteger refRes = harmonic1000(i);
+ String refString = refRes.toString();
+ // Disguise refRes to avoid compiler optimization issues.
+ BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30);
+ // The time-consuming part:
+ String newString = newRes.toString();
+ }
+ }
+ }
+
+ // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them
+ // integers. exp >= 0 .
+ private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) {
+ if (exp == 0) {
+ return scaleBy; // Return one.
+ } else if ((exp & 1) != 0) {
+ BigInteger tmp = myPow(base, exp - 1, scaleBy);
+ return tmp.multiply(base).divide(scaleBy);
+ } else {
+ BigInteger tmp = myPow(base, exp / 2, scaleBy);
+ return tmp.multiply(tmp).divide(scaleBy);
+ }
+ }
+
+ // Approximate e by computing (1 + 1/n)^n to prec decimal digits.
+ // This isn't necessarily a very good approximation to e.
+ // Return the result, scaled by 10^prec.
+ private static BigInteger eApprox(int n, int prec) {
+ BigInteger scaledOne = BigInteger.TEN.pow(prec);
+ BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n)));
+ return myPow(base, n, scaledOne);
+ }
+
+ // Repeatedly execute and check the above, printing one of the results
+ // to compare to reference.
+ @Test
+ public void repeatEApprox() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ BigInteger refRes = eApprox(100_000, i);
+ BigInteger newRes = eApprox(100_000, i);
+ if (!newRes.equals(refRes)) {
+ throw new AssertionError(newRes + " != " + refRes);
+ }
+ if (i >= 10 && !refRes.toString().startsWith("271826")) {
+ throw new AssertionError(
+ "eApprox(" + 100_000 + "," + i + ") incorrectly produced " + refRes);
+ }
+ }
+ }
+ }
+
+ // Test / time modPow()
+ @Test
+ public void repeatModPow() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 500; i *= 10) {
+ BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+ BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+ BigInteger product = odd1.multiply(odd2);
+ BigInteger exponent = BigInteger.TEN.pow(i / 2 - 1);
+ BigInteger base = BigInteger.TEN.pow(i / 4);
+ BigInteger newRes = base.modPow(exponent, product);
+ if (!newRes.mod(odd1).equals(base.modPow(exponent, odd1))) {
+ throw new AssertionError(
+ "ModPow() result incorrect mod odd1:"
+ + odd1
+ + "; lastRes.mod(odd1)="
+ + newRes.mod(odd1)
+ + " vs. "
+ + "base.modPow(exponent, odd1)="
+ + base.modPow(exponent, odd1)
+ + " base="
+ + base
+ + " exponent="
+ + exponent);
+ }
+ if (!newRes.mod(odd2).equals(base.modPow(exponent, odd2))) {
+ throw new AssertionError("ModPow() result incorrect mod odd2");
+ }
+ }
+ }
+ }
+
+ // Test / time modInverse()
+ @Test
+ public void repeatModInverse() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+ BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+ BigInteger product = odd1.multiply(odd2);
+ BigInteger arg = BigInteger.ONE.shiftLeft(i / 4);
+ BigInteger lastRes = null;
+ BigInteger newRes = arg.modInverse(product);
+ lastRes = newRes;
+ if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) {
+ throw new AssertionError("ModInverse() result incorrect mod odd1");
+ }
+ if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) {
+ throw new AssertionError("ModInverse() result incorrect mod odd2");
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
new file mode 100644
index 000000000000..04ef09e4b682
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
@@ -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.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public final class BufferedZipFilePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ int[] mReadSize = new int[] {4, 32, 128};
+ int[] mCompressedSize = new int[] {128, 1024, 8192, 65536};
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile("BufferedZipFilePerfTest", ".zip");
+ mFile.deleteOnExit();
+ Random random = new Random(0);
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(mFile));
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ byte[] data = new byte[8192];
+ out.putNextEntry(new ZipEntry("entry.data" + mCompressedSize[i]));
+ int written = 0;
+ while (written < mCompressedSize[i]) {
+ random.nextBytes(data);
+ int toWrite = Math.min(mCompressedSize[i] - written, data.length);
+ out.write(data, 0, toWrite);
+ written += toWrite;
+ }
+ }
+ out.close();
+ }
+
+ @Test
+ public void timeUnbufferedRead() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ for (int j = 0; j < mReadSize.length; j++) {
+ ZipFile zipFile = new ZipFile(mFile);
+ ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+ InputStream in = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[mReadSize[j]];
+ while (in.read(buffer) != -1) {
+ // Keep reading
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+ }
+ }
+
+ @Test
+ public void timeBufferedRead() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ for (int j = 0; j < mReadSize.length; j++) {
+ ZipFile zipFile = new ZipFile(mFile);
+ ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+ InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
+ byte[] buffer = new byte[mReadSize[j]];
+ while (in.read(buffer) != -1) {
+ // Keep reading
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
new file mode 100644
index 000000000000..4ae88b88b090
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClassLoaderResourcePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
+ private static final String MISSING_RESOURCE = "missing_entry";
+
+ @Test
+ public void timeGetBootResource_hit() {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ currentClassLoader.getResource(EXISTENT_RESOURCE);
+ }
+ }
+
+ @Test
+ public void timeGetBootResource_miss() {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ currentClassLoader.getResource(MISSING_RESOURCE);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
new file mode 100644
index 000000000000..5e73916d5f5b
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
@@ -0,0 +1,1197 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClonePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ static class CloneableObject implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ static class CloneableManyFieldObject implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ Object mO1 = new Object();
+ Object mO2 = new Object();
+ Object mO3 = new Object();
+ Object mO4 = new Object();
+ Object mO5 = new Object();
+ Object mO6 = new Object();
+ Object mO7 = new Object();
+ Object mO8 = new Object();
+ Object mO9 = new Object();
+ Object mO10 = new Object();
+ Object mO11 = new Object();
+ Object mO12 = new Object();
+ Object mO13 = new Object();
+ Object mO14 = new Object();
+ Object mO15 = new Object();
+ Object mO16 = new Object();
+ Object mO17 = new Object();
+ Object mO18 = new Object();
+ Object mO19 = new Object();
+ Object mO20 = new Object();
+ Object mO21 = new Object();
+ Object mO22 = new Object();
+ Object mO23 = new Object();
+ Object mO24 = new Object();
+ Object mO25 = new Object();
+ Object mO26 = new Object();
+ Object mO27 = new Object();
+ Object mO28 = new Object();
+ Object mO29 = new Object();
+ Object mO30 = new Object();
+ Object mO31 = new Object();
+ Object mO32 = new Object();
+ Object mO33 = new Object();
+ Object mO34 = new Object();
+ Object mO35 = new Object();
+ Object mO36 = new Object();
+ Object mO37 = new Object();
+ Object mO38 = new Object();
+ Object mO39 = new Object();
+ Object mO40 = new Object();
+ Object mO41 = new Object();
+ Object mO42 = new Object();
+ Object mO43 = new Object();
+ Object mO44 = new Object();
+ Object mO45 = new Object();
+ Object mO46 = new Object();
+ Object mO47 = new Object();
+ Object mO48 = new Object();
+ Object mO49 = new Object();
+ Object mO50 = new Object();
+ Object mO51 = new Object();
+ Object mO52 = new Object();
+ Object mO53 = new Object();
+ Object mO54 = new Object();
+ Object mO55 = new Object();
+ Object mO56 = new Object();
+ Object mO57 = new Object();
+ Object mO58 = new Object();
+ Object mO59 = new Object();
+ Object mO60 = new Object();
+ Object mO61 = new Object();
+ Object mO62 = new Object();
+ Object mO63 = new Object();
+ Object mO64 = new Object();
+ Object mO65 = new Object();
+ Object mO66 = new Object();
+ Object mO67 = new Object();
+ Object mO68 = new Object();
+ Object mO69 = new Object();
+ Object mO70 = new Object();
+ Object mO71 = new Object();
+ Object mO72 = new Object();
+ Object mO73 = new Object();
+ Object mO74 = new Object();
+ Object mO75 = new Object();
+ Object mO76 = new Object();
+ Object mO77 = new Object();
+ Object mO78 = new Object();
+ Object mO79 = new Object();
+ Object mO80 = new Object();
+ Object mO81 = new Object();
+ Object mO82 = new Object();
+ Object mO83 = new Object();
+ Object mO84 = new Object();
+ Object mO85 = new Object();
+ Object mO86 = new Object();
+ Object mO87 = new Object();
+ Object mO88 = new Object();
+ Object mO89 = new Object();
+ Object mO90 = new Object();
+ Object mO91 = new Object();
+ Object mO92 = new Object();
+ Object mO93 = new Object();
+ Object mO94 = new Object();
+ Object mO95 = new Object();
+ Object mO96 = new Object();
+ Object mO97 = new Object();
+ Object mO98 = new Object();
+ Object mO99 = new Object();
+ Object mO100 = new Object();
+ Object mO101 = new Object();
+ Object mO102 = new Object();
+ Object mO103 = new Object();
+ Object mO104 = new Object();
+ Object mO105 = new Object();
+ Object mO106 = new Object();
+ Object mO107 = new Object();
+ Object mO108 = new Object();
+ Object mO109 = new Object();
+ Object mO110 = new Object();
+ Object mO111 = new Object();
+ Object mO112 = new Object();
+ Object mO113 = new Object();
+ Object mO114 = new Object();
+ Object mO115 = new Object();
+ Object mO116 = new Object();
+ Object mO117 = new Object();
+ Object mO118 = new Object();
+ Object mO119 = new Object();
+ Object mO120 = new Object();
+ Object mO121 = new Object();
+ Object mO122 = new Object();
+ Object mO123 = new Object();
+ Object mO124 = new Object();
+ Object mO125 = new Object();
+ Object mO126 = new Object();
+ Object mO127 = new Object();
+ Object mO128 = new Object();
+ Object mO129 = new Object();
+ Object mO130 = new Object();
+ Object mO131 = new Object();
+ Object mO132 = new Object();
+ Object mO133 = new Object();
+ Object mO134 = new Object();
+ Object mO135 = new Object();
+ Object mO136 = new Object();
+ Object mO137 = new Object();
+ Object mO138 = new Object();
+ Object mO139 = new Object();
+ Object mO140 = new Object();
+ Object mO141 = new Object();
+ Object mO142 = new Object();
+ Object mO143 = new Object();
+ Object mO144 = new Object();
+ Object mO145 = new Object();
+ Object mO146 = new Object();
+ Object mO147 = new Object();
+ Object mO148 = new Object();
+ Object mO149 = new Object();
+ Object mO150 = new Object();
+ Object mO151 = new Object();
+ Object mO152 = new Object();
+ Object mO153 = new Object();
+ Object mO154 = new Object();
+ Object mO155 = new Object();
+ Object mO156 = new Object();
+ Object mO157 = new Object();
+ Object mO158 = new Object();
+ Object mO159 = new Object();
+ Object mO160 = new Object();
+ Object mO161 = new Object();
+ Object mO162 = new Object();
+ Object mO163 = new Object();
+ Object mO164 = new Object();
+ Object mO165 = new Object();
+ Object mO166 = new Object();
+ Object mO167 = new Object();
+ Object mO168 = new Object();
+ Object mO169 = new Object();
+ Object mO170 = new Object();
+ Object mO171 = new Object();
+ Object mO172 = new Object();
+ Object mO173 = new Object();
+ Object mO174 = new Object();
+ Object mO175 = new Object();
+ Object mO176 = new Object();
+ Object mO177 = new Object();
+ Object mO178 = new Object();
+ Object mO179 = new Object();
+ Object mO180 = new Object();
+ Object mO181 = new Object();
+ Object mO182 = new Object();
+ Object mO183 = new Object();
+ Object mO184 = new Object();
+ Object mO185 = new Object();
+ Object mO186 = new Object();
+ Object mO187 = new Object();
+ Object mO188 = new Object();
+ Object mO189 = new Object();
+ Object mO190 = new Object();
+ Object mO191 = new Object();
+ Object mO192 = new Object();
+ Object mO193 = new Object();
+ Object mO194 = new Object();
+ Object mO195 = new Object();
+ Object mO196 = new Object();
+ Object mO197 = new Object();
+ Object mO198 = new Object();
+ Object mO199 = new Object();
+ Object mO200 = new Object();
+ Object mO201 = new Object();
+ Object mO202 = new Object();
+ Object mO203 = new Object();
+ Object mO204 = new Object();
+ Object mO205 = new Object();
+ Object mO206 = new Object();
+ Object mO207 = new Object();
+ Object mO208 = new Object();
+ Object mO209 = new Object();
+ Object mO210 = new Object();
+ Object mO211 = new Object();
+ Object mO212 = new Object();
+ Object mO213 = new Object();
+ Object mO214 = new Object();
+ Object mO215 = new Object();
+ Object mO216 = new Object();
+ Object mO217 = new Object();
+ Object mO218 = new Object();
+ Object mO219 = new Object();
+ Object mO220 = new Object();
+ Object mO221 = new Object();
+ Object mO222 = new Object();
+ Object mO223 = new Object();
+ Object mO224 = new Object();
+ Object mO225 = new Object();
+ Object mO226 = new Object();
+ Object mO227 = new Object();
+ Object mO228 = new Object();
+ Object mO229 = new Object();
+ Object mO230 = new Object();
+ Object mO231 = new Object();
+ Object mO232 = new Object();
+ Object mO233 = new Object();
+ Object mO234 = new Object();
+ Object mO235 = new Object();
+ Object mO236 = new Object();
+ Object mO237 = new Object();
+ Object mO238 = new Object();
+ Object mO239 = new Object();
+ Object mO240 = new Object();
+ Object mO241 = new Object();
+ Object mO242 = new Object();
+ Object mO243 = new Object();
+ Object mO244 = new Object();
+ Object mO245 = new Object();
+ Object mO246 = new Object();
+ Object mO247 = new Object();
+ Object mO248 = new Object();
+ Object mO249 = new Object();
+ Object mO250 = new Object();
+ Object mO251 = new Object();
+ Object mO252 = new Object();
+ Object mO253 = new Object();
+ Object mO254 = new Object();
+ Object mO255 = new Object();
+ Object mO256 = new Object();
+ Object mO257 = new Object();
+ Object mO258 = new Object();
+ Object mO259 = new Object();
+ Object mO260 = new Object();
+ Object mO261 = new Object();
+ Object mO262 = new Object();
+ Object mO263 = new Object();
+ Object mO264 = new Object();
+ Object mO265 = new Object();
+ Object mO266 = new Object();
+ Object mO267 = new Object();
+ Object mO268 = new Object();
+ Object mO269 = new Object();
+ Object mO270 = new Object();
+ Object mO271 = new Object();
+ Object mO272 = new Object();
+ Object mO273 = new Object();
+ Object mO274 = new Object();
+ Object mO275 = new Object();
+ Object mO276 = new Object();
+ Object mO277 = new Object();
+ Object mO278 = new Object();
+ Object mO279 = new Object();
+ Object mO280 = new Object();
+ Object mO281 = new Object();
+ Object mO282 = new Object();
+ Object mO283 = new Object();
+ Object mO284 = new Object();
+ Object mO285 = new Object();
+ Object mO286 = new Object();
+ Object mO287 = new Object();
+ Object mO288 = new Object();
+ Object mO289 = new Object();
+ Object mO290 = new Object();
+ Object mO291 = new Object();
+ Object mO292 = new Object();
+ Object mO293 = new Object();
+ Object mO294 = new Object();
+ Object mO295 = new Object();
+ Object mO296 = new Object();
+ Object mO297 = new Object();
+ Object mO298 = new Object();
+ Object mO299 = new Object();
+ Object mO300 = new Object();
+ Object mO301 = new Object();
+ Object mO302 = new Object();
+ Object mO303 = new Object();
+ Object mO304 = new Object();
+ Object mO305 = new Object();
+ Object mO306 = new Object();
+ Object mO307 = new Object();
+ Object mO308 = new Object();
+ Object mO309 = new Object();
+ Object mO310 = new Object();
+ Object mO311 = new Object();
+ Object mO312 = new Object();
+ Object mO313 = new Object();
+ Object mO314 = new Object();
+ Object mO315 = new Object();
+ Object mO316 = new Object();
+ Object mO317 = new Object();
+ Object mO318 = new Object();
+ Object mO319 = new Object();
+ Object mO320 = new Object();
+ Object mO321 = new Object();
+ Object mO322 = new Object();
+ Object mO323 = new Object();
+ Object mO324 = new Object();
+ Object mO325 = new Object();
+ Object mO326 = new Object();
+ Object mO327 = new Object();
+ Object mO328 = new Object();
+ Object mO329 = new Object();
+ Object mO330 = new Object();
+ Object mO331 = new Object();
+ Object mO332 = new Object();
+ Object mO333 = new Object();
+ Object mO334 = new Object();
+ Object mO335 = new Object();
+ Object mO336 = new Object();
+ Object mO337 = new Object();
+ Object mO338 = new Object();
+ Object mO339 = new Object();
+ Object mO340 = new Object();
+ Object mO341 = new Object();
+ Object mO342 = new Object();
+ Object mO343 = new Object();
+ Object mO344 = new Object();
+ Object mO345 = new Object();
+ Object mO346 = new Object();
+ Object mO347 = new Object();
+ Object mO348 = new Object();
+ Object mO349 = new Object();
+ Object mO350 = new Object();
+ Object mO351 = new Object();
+ Object mO352 = new Object();
+ Object mO353 = new Object();
+ Object mO354 = new Object();
+ Object mO355 = new Object();
+ Object mO356 = new Object();
+ Object mO357 = new Object();
+ Object mO358 = new Object();
+ Object mO359 = new Object();
+ Object mO360 = new Object();
+ Object mO361 = new Object();
+ Object mO362 = new Object();
+ Object mO363 = new Object();
+ Object mO364 = new Object();
+ Object mO365 = new Object();
+ Object mO366 = new Object();
+ Object mO367 = new Object();
+ Object mO368 = new Object();
+ Object mO369 = new Object();
+ Object mO370 = new Object();
+ Object mO371 = new Object();
+ Object mO372 = new Object();
+ Object mO373 = new Object();
+ Object mO374 = new Object();
+ Object mO375 = new Object();
+ Object mO376 = new Object();
+ Object mO377 = new Object();
+ Object mO378 = new Object();
+ Object mO379 = new Object();
+ Object mO380 = new Object();
+ Object mO381 = new Object();
+ Object mO382 = new Object();
+ Object mO383 = new Object();
+ Object mO384 = new Object();
+ Object mO385 = new Object();
+ Object mO386 = new Object();
+ Object mO387 = new Object();
+ Object mO388 = new Object();
+ Object mO389 = new Object();
+ Object mO390 = new Object();
+ Object mO391 = new Object();
+ Object mO392 = new Object();
+ Object mO393 = new Object();
+ Object mO394 = new Object();
+ Object mO395 = new Object();
+ Object mO396 = new Object();
+ Object mO397 = new Object();
+ Object mO398 = new Object();
+ Object mO399 = new Object();
+ Object mO400 = new Object();
+ Object mO401 = new Object();
+ Object mO402 = new Object();
+ Object mO403 = new Object();
+ Object mO404 = new Object();
+ Object mO405 = new Object();
+ Object mO406 = new Object();
+ Object mO407 = new Object();
+ Object mO408 = new Object();
+ Object mO409 = new Object();
+ Object mO410 = new Object();
+ Object mO411 = new Object();
+ Object mO412 = new Object();
+ Object mO413 = new Object();
+ Object mO414 = new Object();
+ Object mO415 = new Object();
+ Object mO416 = new Object();
+ Object mO417 = new Object();
+ Object mO418 = new Object();
+ Object mO419 = new Object();
+ Object mO420 = new Object();
+ Object mO421 = new Object();
+ Object mO422 = new Object();
+ Object mO423 = new Object();
+ Object mO424 = new Object();
+ Object mO425 = new Object();
+ Object mO426 = new Object();
+ Object mO427 = new Object();
+ Object mO428 = new Object();
+ Object mO429 = new Object();
+ Object mO430 = new Object();
+ Object mO431 = new Object();
+ Object mO432 = new Object();
+ Object mO433 = new Object();
+ Object mO434 = new Object();
+ Object mO435 = new Object();
+ Object mO436 = new Object();
+ Object mO437 = new Object();
+ Object mO438 = new Object();
+ Object mO439 = new Object();
+ Object mO440 = new Object();
+ Object mO441 = new Object();
+ Object mO442 = new Object();
+ Object mO460 = new Object();
+ Object mO461 = new Object();
+ Object mO462 = new Object();
+ Object mO463 = new Object();
+ Object mO464 = new Object();
+ Object mO465 = new Object();
+ Object mO466 = new Object();
+ Object mO467 = new Object();
+ Object mO468 = new Object();
+ Object mO469 = new Object();
+ Object mO470 = new Object();
+ Object mO471 = new Object();
+ Object mO472 = new Object();
+ Object mO473 = new Object();
+ Object mO474 = new Object();
+ Object mO475 = new Object();
+ Object mO476 = new Object();
+ Object mO477 = new Object();
+ Object mO478 = new Object();
+ Object mO479 = new Object();
+ Object mO480 = new Object();
+ Object mO481 = new Object();
+ Object mO482 = new Object();
+ Object mO483 = new Object();
+ Object mO484 = new Object();
+ Object mO485 = new Object();
+ Object mO486 = new Object();
+ Object mO487 = new Object();
+ Object mO488 = new Object();
+ Object mO489 = new Object();
+ Object mO490 = new Object();
+ Object mO491 = new Object();
+ Object mO492 = new Object();
+ Object mO493 = new Object();
+ Object mO494 = new Object();
+ Object mO495 = new Object();
+ Object mO496 = new Object();
+ Object mO497 = new Object();
+ Object mO498 = new Object();
+ Object mO499 = new Object();
+ Object mO500 = new Object();
+ Object mO501 = new Object();
+ Object mO502 = new Object();
+ Object mO503 = new Object();
+ Object mO504 = new Object();
+ Object mO505 = new Object();
+ Object mO506 = new Object();
+ Object mO507 = new Object();
+ Object mO508 = new Object();
+ Object mO509 = new Object();
+ Object mO510 = new Object();
+ Object mO511 = new Object();
+ Object mO512 = new Object();
+ Object mO513 = new Object();
+ Object mO514 = new Object();
+ Object mO515 = new Object();
+ Object mO516 = new Object();
+ Object mO517 = new Object();
+ Object mO518 = new Object();
+ Object mO519 = new Object();
+ Object mO520 = new Object();
+ Object mO521 = new Object();
+ Object mO522 = new Object();
+ Object mO523 = new Object();
+ Object mO556 = new Object();
+ Object mO557 = new Object();
+ Object mO558 = new Object();
+ Object mO559 = new Object();
+ Object mO560 = new Object();
+ Object mO561 = new Object();
+ Object mO562 = new Object();
+ Object mO563 = new Object();
+ Object mO564 = new Object();
+ Object mO565 = new Object();
+ Object mO566 = new Object();
+ Object mO567 = new Object();
+ Object mO568 = new Object();
+ Object mO569 = new Object();
+ Object mO570 = new Object();
+ Object mO571 = new Object();
+ Object mO572 = new Object();
+ Object mO573 = new Object();
+ Object mO574 = new Object();
+ Object mO575 = new Object();
+ Object mO576 = new Object();
+ Object mO577 = new Object();
+ Object mO578 = new Object();
+ Object mO579 = new Object();
+ Object mO580 = new Object();
+ Object mO581 = new Object();
+ Object mO582 = new Object();
+ Object mO583 = new Object();
+ Object mO584 = new Object();
+ Object mO585 = new Object();
+ Object mO586 = new Object();
+ Object mO587 = new Object();
+ Object mO588 = new Object();
+ Object mO589 = new Object();
+ Object mO590 = new Object();
+ Object mO591 = new Object();
+ Object mO592 = new Object();
+ Object mO593 = new Object();
+ Object mO594 = new Object();
+ Object mO595 = new Object();
+ Object mO596 = new Object();
+ Object mO597 = new Object();
+ Object mO598 = new Object();
+ Object mO599 = new Object();
+ Object mO600 = new Object();
+ Object mO601 = new Object();
+ Object mO602 = new Object();
+ Object mO603 = new Object();
+ Object mO604 = new Object();
+ Object mO605 = new Object();
+ Object mO606 = new Object();
+ Object mO607 = new Object();
+ Object mO608 = new Object();
+ Object mO609 = new Object();
+ Object mO610 = new Object();
+ Object mO611 = new Object();
+ Object mO612 = new Object();
+ Object mO613 = new Object();
+ Object mO614 = new Object();
+ Object mO615 = new Object();
+ Object mO616 = new Object();
+ Object mO617 = new Object();
+ Object mO618 = new Object();
+ Object mO619 = new Object();
+ Object mO620 = new Object();
+ Object mO621 = new Object();
+ Object mO622 = new Object();
+ Object mO623 = new Object();
+ Object mO624 = new Object();
+ Object mO625 = new Object();
+ Object mO626 = new Object();
+ Object mO627 = new Object();
+ Object mO628 = new Object();
+ Object mO629 = new Object();
+ Object mO630 = new Object();
+ Object mO631 = new Object();
+ Object mO632 = new Object();
+ Object mO633 = new Object();
+ Object mO634 = new Object();
+ Object mO635 = new Object();
+ Object mO636 = new Object();
+ Object mO637 = new Object();
+ Object mO638 = new Object();
+ Object mO639 = new Object();
+ Object mO640 = new Object();
+ Object mO641 = new Object();
+ Object mO642 = new Object();
+ Object mO643 = new Object();
+ Object mO644 = new Object();
+ Object mO645 = new Object();
+ Object mO646 = new Object();
+ Object mO647 = new Object();
+ Object mO648 = new Object();
+ Object mO649 = new Object();
+ Object mO650 = new Object();
+ Object mO651 = new Object();
+ Object mO652 = new Object();
+ Object mO653 = new Object();
+ Object mO654 = new Object();
+ Object mO655 = new Object();
+ Object mO656 = new Object();
+ Object mO657 = new Object();
+ Object mO658 = new Object();
+ Object mO659 = new Object();
+ Object mO660 = new Object();
+ Object mO661 = new Object();
+ Object mO662 = new Object();
+ Object mO663 = new Object();
+ Object mO664 = new Object();
+ Object mO665 = new Object();
+ Object mO666 = new Object();
+ Object mO667 = new Object();
+ Object mO668 = new Object();
+ Object mO669 = new Object();
+ Object mO670 = new Object();
+ Object mO671 = new Object();
+ Object mO672 = new Object();
+ Object mO673 = new Object();
+ Object mO674 = new Object();
+ Object mO675 = new Object();
+ Object mO676 = new Object();
+ Object mO677 = new Object();
+ Object mO678 = new Object();
+ Object mO679 = new Object();
+ Object mO680 = new Object();
+ Object mO681 = new Object();
+ Object mO682 = new Object();
+ Object mO683 = new Object();
+ Object mO684 = new Object();
+ Object mO685 = new Object();
+ Object mO686 = new Object();
+ Object mO687 = new Object();
+ Object mO688 = new Object();
+ Object mO734 = new Object();
+ Object mO735 = new Object();
+ Object mO736 = new Object();
+ Object mO737 = new Object();
+ Object mO738 = new Object();
+ Object mO739 = new Object();
+ Object mO740 = new Object();
+ Object mO741 = new Object();
+ Object mO742 = new Object();
+ Object mO743 = new Object();
+ Object mO744 = new Object();
+ Object mO745 = new Object();
+ Object mO746 = new Object();
+ Object mO747 = new Object();
+ Object mO748 = new Object();
+ Object mO749 = new Object();
+ Object mO750 = new Object();
+ Object mO751 = new Object();
+ Object mO752 = new Object();
+ Object mO753 = new Object();
+ Object mO754 = new Object();
+ Object mO755 = new Object();
+ Object mO756 = new Object();
+ Object mO757 = new Object();
+ Object mO758 = new Object();
+ Object mO759 = new Object();
+ Object mO760 = new Object();
+ Object mO761 = new Object();
+ Object mO762 = new Object();
+ Object mO763 = new Object();
+ Object mO764 = new Object();
+ Object mO765 = new Object();
+ Object mO766 = new Object();
+ Object mO767 = new Object();
+ Object mO768 = new Object();
+ Object mO769 = new Object();
+ Object mO770 = new Object();
+ Object mO771 = new Object();
+ Object mO772 = new Object();
+ Object mO773 = new Object();
+ Object mO774 = new Object();
+ Object mO775 = new Object();
+ Object mO776 = new Object();
+ Object mO777 = new Object();
+ Object mO778 = new Object();
+ Object mO779 = new Object();
+ Object mO780 = new Object();
+ Object mO781 = new Object();
+ Object mO782 = new Object();
+ Object mO783 = new Object();
+ Object mO784 = new Object();
+ Object mO785 = new Object();
+ Object mO786 = new Object();
+ Object mO787 = new Object();
+ Object mO788 = new Object();
+ Object mO789 = new Object();
+ Object mO790 = new Object();
+ Object mO791 = new Object();
+ Object mO792 = new Object();
+ Object mO793 = new Object();
+ Object mO794 = new Object();
+ Object mO795 = new Object();
+ Object mO796 = new Object();
+ Object mO797 = new Object();
+ Object mO798 = new Object();
+ Object mO799 = new Object();
+ Object mO800 = new Object();
+ Object mO801 = new Object();
+ Object mO802 = new Object();
+ Object mO803 = new Object();
+ Object mO804 = new Object();
+ Object mO805 = new Object();
+ Object mO806 = new Object();
+ Object mO807 = new Object();
+ Object mO808 = new Object();
+ Object mO809 = new Object();
+ Object mO810 = new Object();
+ Object mO811 = new Object();
+ Object mO812 = new Object();
+ Object mO813 = new Object();
+ Object mO848 = new Object();
+ Object mO849 = new Object();
+ Object mO850 = new Object();
+ Object mO851 = new Object();
+ Object mO852 = new Object();
+ Object mO853 = new Object();
+ Object mO854 = new Object();
+ Object mO855 = new Object();
+ Object mO856 = new Object();
+ Object mO857 = new Object();
+ Object mO858 = new Object();
+ Object mO859 = new Object();
+ Object mO860 = new Object();
+ Object mO861 = new Object();
+ Object mO862 = new Object();
+ Object mO863 = new Object();
+ Object mO864 = new Object();
+ Object mO865 = new Object();
+ Object mO866 = new Object();
+ Object mO867 = new Object();
+ Object mO868 = new Object();
+ Object mO869 = new Object();
+ Object mO870 = new Object();
+ Object mO871 = new Object();
+ Object mO872 = new Object();
+ Object mO873 = new Object();
+ Object mO874 = new Object();
+ Object mO875 = new Object();
+ Object mO876 = new Object();
+ Object mO877 = new Object();
+ Object mO878 = new Object();
+ Object mO879 = new Object();
+ Object mO880 = new Object();
+ Object mO881 = new Object();
+ Object mO882 = new Object();
+ Object mO883 = new Object();
+ Object mO884 = new Object();
+ Object mO885 = new Object();
+ Object mO886 = new Object();
+ Object mO887 = new Object();
+ Object mO888 = new Object();
+ Object mO889 = new Object();
+ Object mO890 = new Object();
+ Object mO891 = new Object();
+ Object mO892 = new Object();
+ Object mO893 = new Object();
+ Object mO894 = new Object();
+ Object mO895 = new Object();
+ Object mO896 = new Object();
+ Object mO897 = new Object();
+ Object mO898 = new Object();
+ Object mO899 = new Object();
+ Object mO900 = new Object();
+ Object mO901 = new Object();
+ Object mO902 = new Object();
+ Object mO903 = new Object();
+ Object mO904 = new Object();
+ Object mO905 = new Object();
+ Object mO906 = new Object();
+ Object mO907 = new Object();
+ Object mO908 = new Object();
+ Object mO909 = new Object();
+ Object mO910 = new Object();
+ Object mO911 = new Object();
+ Object mO912 = new Object();
+ Object mO913 = new Object();
+ Object mO914 = new Object();
+ Object mO915 = new Object();
+ Object mO916 = new Object();
+ Object mO917 = new Object();
+ Object mO918 = new Object();
+ Object mO919 = new Object();
+ Object mO920 = new Object();
+ Object mO921 = new Object();
+ Object mO922 = new Object();
+ Object mO923 = new Object();
+ Object mO924 = new Object();
+ Object mO925 = new Object();
+ Object mO926 = new Object();
+ Object mO927 = new Object();
+ Object mO928 = new Object();
+ Object mO929 = new Object();
+ Object mO930 = new Object();
+ Object mO931 = new Object();
+ Object mO932 = new Object();
+ Object mO933 = new Object();
+ Object mO934 = new Object();
+ Object mO935 = new Object();
+ Object mO936 = new Object();
+ Object mO937 = new Object();
+ Object mO938 = new Object();
+ Object mO939 = new Object();
+ Object mO940 = new Object();
+ Object mO941 = new Object();
+ Object mO942 = new Object();
+ Object mO943 = new Object();
+ Object mO944 = new Object();
+ Object mO945 = new Object();
+ Object mO946 = new Object();
+ Object mO947 = new Object();
+ Object mO948 = new Object();
+ Object mO949 = new Object();
+ Object mO950 = new Object();
+ Object mO951 = new Object();
+ Object mO952 = new Object();
+ Object mO953 = new Object();
+ Object mO954 = new Object();
+ Object mO955 = new Object();
+ Object mO956 = new Object();
+ Object mO957 = new Object();
+ Object mO958 = new Object();
+ Object mO959 = new Object();
+ Object mO960 = new Object();
+ Object mO961 = new Object();
+ Object mO962 = new Object();
+ Object mO963 = new Object();
+ Object mO964 = new Object();
+ Object mO965 = new Object();
+ Object mO966 = new Object();
+ Object mO967 = new Object();
+ Object mO968 = new Object();
+ Object mO969 = new Object();
+ Object mO970 = new Object();
+ Object mO971 = new Object();
+ Object mO972 = new Object();
+ Object mO973 = new Object();
+ Object mO974 = new Object();
+ Object mO975 = new Object();
+ Object mO976 = new Object();
+ Object mO977 = new Object();
+ Object mO978 = new Object();
+ Object mO979 = new Object();
+ Object mO980 = new Object();
+ Object mO981 = new Object();
+ Object mO982 = new Object();
+ Object mO983 = new Object();
+ Object mO984 = new Object();
+ Object mO985 = new Object();
+ Object mO986 = new Object();
+ Object mO987 = new Object();
+ Object mO988 = new Object();
+ Object mO989 = new Object();
+ Object mO990 = new Object();
+ Object mO991 = new Object();
+ Object mO992 = new Object();
+ Object mO993 = new Object();
+ Object mO994 = new Object();
+ Object mO995 = new Object();
+ Object mO996 = new Object();
+ Object mO997 = new Object();
+ Object mO998 = new Object();
+ Object mO999 = new Object();
+ }
+
+ static class Deep0 {}
+
+ static class Deep1 extends Deep0 {}
+
+ static class Deep2 extends Deep1 {}
+
+ static class Deep3 extends Deep2 {}
+
+ static class Deep4 extends Deep3 {}
+
+ static class Deep5 extends Deep4 {}
+
+ static class Deep6 extends Deep5 {}
+
+ static class Deep7 extends Deep6 {}
+
+ static class Deep8 extends Deep7 {}
+
+ static class Deep9 extends Deep8 {}
+
+ static class Deep10 extends Deep9 {}
+
+ static class Deep11 extends Deep10 {}
+
+ static class Deep12 extends Deep11 {}
+
+ static class Deep13 extends Deep12 {}
+
+ static class Deep14 extends Deep13 {}
+
+ static class Deep15 extends Deep14 {}
+
+ static class Deep16 extends Deep15 {}
+
+ static class Deep17 extends Deep16 {}
+
+ static class Deep18 extends Deep17 {}
+
+ static class Deep19 extends Deep18 {}
+
+ static class Deep20 extends Deep19 {}
+
+ static class Deep21 extends Deep20 {}
+
+ static class Deep22 extends Deep21 {}
+
+ static class Deep23 extends Deep22 {}
+
+ static class Deep24 extends Deep23 {}
+
+ static class Deep25 extends Deep24 {}
+
+ static class Deep26 extends Deep25 {}
+
+ static class Deep27 extends Deep26 {}
+
+ static class Deep28 extends Deep27 {}
+
+ static class Deep29 extends Deep28 {}
+
+ static class Deep30 extends Deep29 {}
+
+ static class Deep31 extends Deep30 {}
+
+ static class Deep32 extends Deep31 {}
+
+ static class Deep33 extends Deep32 {}
+
+ static class Deep34 extends Deep33 {}
+
+ static class Deep35 extends Deep34 {}
+
+ static class Deep36 extends Deep35 {}
+
+ static class Deep37 extends Deep36 {}
+
+ static class Deep38 extends Deep37 {}
+
+ static class Deep39 extends Deep38 {}
+
+ static class Deep40 extends Deep39 {}
+
+ static class Deep41 extends Deep40 {}
+
+ static class Deep42 extends Deep41 {}
+
+ static class Deep43 extends Deep42 {}
+
+ static class Deep44 extends Deep43 {}
+
+ static class Deep45 extends Deep44 {}
+
+ static class Deep46 extends Deep45 {}
+
+ static class Deep47 extends Deep46 {}
+
+ static class Deep48 extends Deep47 {}
+
+ static class Deep49 extends Deep48 {}
+
+ static class Deep50 extends Deep49 {}
+
+ static class Deep51 extends Deep50 {}
+
+ static class Deep52 extends Deep51 {}
+
+ static class Deep53 extends Deep52 {}
+
+ static class Deep54 extends Deep53 {}
+
+ static class Deep55 extends Deep54 {}
+
+ static class Deep56 extends Deep55 {}
+
+ static class Deep57 extends Deep56 {}
+
+ static class Deep58 extends Deep57 {}
+
+ static class Deep59 extends Deep58 {}
+
+ static class Deep60 extends Deep59 {}
+
+ static class Deep61 extends Deep60 {}
+
+ static class Deep62 extends Deep61 {}
+
+ static class Deep63 extends Deep62 {}
+
+ static class Deep64 extends Deep63 {}
+
+ static class Deep65 extends Deep64 {}
+
+ static class Deep66 extends Deep65 {}
+
+ static class Deep67 extends Deep66 {}
+
+ static class Deep68 extends Deep67 {}
+
+ static class Deep69 extends Deep68 {}
+
+ static class Deep70 extends Deep69 {}
+
+ static class Deep71 extends Deep70 {}
+
+ static class Deep72 extends Deep71 {}
+
+ static class Deep73 extends Deep72 {}
+
+ static class Deep74 extends Deep73 {}
+
+ static class Deep75 extends Deep74 {}
+
+ static class Deep76 extends Deep75 {}
+
+ static class Deep77 extends Deep76 {}
+
+ static class Deep78 extends Deep77 {}
+
+ static class Deep79 extends Deep78 {}
+
+ static class Deep80 extends Deep79 {}
+
+ static class Deep81 extends Deep80 {}
+
+ static class Deep82 extends Deep81 {}
+
+ static class Deep83 extends Deep82 {}
+
+ static class Deep84 extends Deep83 {}
+
+ static class Deep85 extends Deep84 {}
+
+ static class Deep86 extends Deep85 {}
+
+ static class Deep87 extends Deep86 {}
+
+ static class Deep88 extends Deep87 {}
+
+ static class Deep89 extends Deep88 {}
+
+ static class Deep90 extends Deep89 {}
+
+ static class Deep91 extends Deep90 {}
+
+ static class Deep92 extends Deep91 {}
+
+ static class Deep93 extends Deep92 {}
+
+ static class Deep94 extends Deep93 {}
+
+ static class Deep95 extends Deep94 {}
+
+ static class Deep96 extends Deep95 {}
+
+ static class Deep97 extends Deep96 {}
+
+ static class Deep98 extends Deep97 {}
+
+ static class Deep99 extends Deep98 {}
+
+ static class Deep100 extends Deep99 {}
+
+ static class DeepCloneable extends Deep100 implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ @Test
+ public void time_Object_clone() {
+ try {
+ CloneableObject o = new CloneableObject();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Object_manyFieldClone() {
+ try {
+ CloneableManyFieldObject o = new CloneableManyFieldObject();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Object_deepClone() {
+ try {
+ DeepCloneable o = new DeepCloneable();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Array_clone() {
+ int[] o = new int[32];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+
+ @Test
+ public void time_ObjectArray_smallClone() {
+ Object[] o = new Object[32];
+ for (int i = 0; i < o.length / 2; ++i) {
+ o[i] = new Object();
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+
+ @Test
+ public void time_ObjectArray_largeClone() {
+ Object[] o = new Object[2048];
+ for (int i = 0; i < o.length / 2; ++i) {
+ o[i] = new Object();
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
new file mode 100644
index 000000000000..3f4f6af7554c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class DeepArrayOpsPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Object[] mArray;
+ private Object[] mArray2;
+
+ @Parameterized.Parameter(0)
+ public int mArrayLength;
+
+ @Parameterized.Parameters(name = "mArrayLength({0})")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}});
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mArray = new Object[mArrayLength * 14];
+ mArray2 = new Object[mArrayLength * 14];
+ for (int i = 0; i < mArrayLength; i += 14) {
+ mArray[i] = new IntWrapper(i);
+ mArray2[i] = new IntWrapper(i);
+
+ mArray[i + 1] = new16ElementObjectmArray();
+ mArray2[i + 1] = new16ElementObjectmArray();
+
+ mArray[i + 2] = new boolean[16];
+ mArray2[i + 2] = new boolean[16];
+
+ mArray[i + 3] = new byte[16];
+ mArray2[i + 3] = new byte[16];
+
+ mArray[i + 4] = new char[16];
+ mArray2[i + 4] = new char[16];
+
+ mArray[i + 5] = new short[16];
+ mArray2[i + 5] = new short[16];
+
+ mArray[i + 6] = new float[16];
+ mArray2[i + 6] = new float[16];
+
+ mArray[i + 7] = new long[16];
+ mArray2[i + 7] = new long[16];
+
+ mArray[i + 8] = new int[16];
+ mArray2[i + 8] = new int[16];
+
+ mArray[i + 9] = new double[16];
+ mArray2[i + 9] = new double[16];
+
+ // SubmArray types are concrete objects.
+ mArray[i + 10] = new16ElementArray(String.class, String.class);
+ mArray2[i + 10] = new16ElementArray(String.class, String.class);
+
+ mArray[i + 11] = new16ElementArray(Integer.class, Integer.class);
+ mArray2[i + 11] = new16ElementArray(Integer.class, Integer.class);
+
+ // SubmArray types is an interface.
+ mArray[i + 12] = new16ElementArray(CharSequence.class, String.class);
+ mArray2[i + 12] = new16ElementArray(CharSequence.class, String.class);
+
+ mArray[i + 13] = null;
+ mArray2[i + 13] = null;
+ }
+ }
+
+ @Test
+ public void deepHashCode() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Arrays.deepHashCode(mArray);
+ }
+ }
+
+ @Test
+ public void deepEquals() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Arrays.deepEquals(mArray, mArray2);
+ }
+ }
+
+ private static Object[] new16ElementObjectmArray() {
+ Object[] array = new Object[16];
+ for (int i = 0; i < 16; ++i) {
+ array[i] = new IntWrapper(i);
+ }
+
+ return array;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T, V> T[] new16ElementArray(Class<T> mArrayType, Class<V> type)
+ throws Exception {
+ T[] array = (T[]) Array.newInstance(type, 16);
+ if (!mArrayType.isAssignableFrom(type)) {
+ throw new IllegalArgumentException(mArrayType + " is not assignable from " + type);
+ }
+
+ Constructor<V> constructor = type.getDeclaredConstructor(String.class);
+ for (int i = 0; i < 16; ++i) {
+ array[i] = (T) constructor.newInstance(String.valueOf(i + 1000));
+ }
+
+ return array;
+ }
+
+ /**
+ * A class that provides very basic equals() and hashCode() operations and doesn't resort to
+ * memoization tricks like {@link java.lang.Integer}.
+ *
+ * <p>Useful for providing equal objects that aren't the same (a.equals(b) but a != b).
+ */
+ public static final class IntWrapper {
+ private final int mWrapped;
+
+ public IntWrapper(int wrap) {
+ mWrapped = wrap;
+ }
+
+ @Override
+ public int hashCode() {
+ return mWrapped;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IntWrapper)) {
+ return false;
+ }
+
+ return ((IntWrapper) o).mWrapped == this.mWrapped;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
new file mode 100644
index 000000000000..da94ae118900
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** What does field access cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class FieldAccessPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private static class Inner {
+ public int mPublicInnerIntVal;
+ protected int mProtectedInnerIntVal;
+ private int mPrivateInnerIntVal;
+ int mPackageInnerIntVal;
+ }
+
+ int mIntVal = 42;
+ final int mFinalIntVal = 42;
+ static int sStaticIntVal = 42;
+ static final int FINAL_INT_VAL = 42;
+
+ @Test
+ public void timeField() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = mIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldFinal() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = mFinalIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldStatic() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = sStaticIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldStaticFinal() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = FINAL_INT_VAL;
+ }
+ }
+
+ @Test
+ public void timeFieldCached() {
+ int result = 0;
+ int cachedIntVal = this.mIntVal;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = cachedIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPublicField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPublicInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassProtectedField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mProtectedInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPrivateField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPrivateInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPackageField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPackageInnerIntVal;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
new file mode 100644
index 000000000000..9446d99c959d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** How do the various hash maps compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class HashedCollectionsPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeHashMapGet() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeHashMapGet_Synchronized() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ synchronized (map) {
+ map.put("hello", "world");
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ synchronized (map) {
+ map.get("hello");
+ }
+ }
+ }
+
+ @Test
+ public void timeHashtableGet() {
+ Hashtable<String, String> map = new Hashtable<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeLinkedHashMapGet() {
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeConcurrentHashMapGet() {
+ ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
new file mode 100644
index 000000000000..be2a7e97f775
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
@@ -0,0 +1,1818 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py. It measures the performance impact of
+ * conflicts in interface method tables. Run `python ImtConflictPerfTestGen.py >
+ * ImtConflictPerfTest.java` to regenerate.
+ *
+ * <p>Each interface has 64 methods, which is the current size of an IMT. C0 implements one
+ * interface, C1 implements two, C2 implements three, and so on. The intent is that C0 has no
+ * conflicts in its IMT, C1 has depth-2 conflicts in its IMT, C2 has depth-3 conflicts, etc. This is
+ * currently guaranteed by the fact that we hash interface methods by taking their method index
+ * modulo 64. (Note that a "conflict depth" of 1 means no conflict at all.)
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ImtConflictPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Before
+ public void setup() {
+ C0 c0 = new C0();
+ callF0(c0);
+ C1 c1 = new C1();
+ callF0(c1);
+ callF19(c1);
+ C2 c2 = new C2();
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ C3 c3 = new C3();
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ C4 c4 = new C4();
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ C5 c5 = new C5();
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ C6 c6 = new C6();
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ C7 c7 = new C7();
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ C8 c8 = new C8();
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ C9 c9 = new C9();
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ C10 c10 = new C10();
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ callF171(c10);
+ callF190(c10);
+ C11 c11 = new C11();
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ callF152(c11);
+ callF171(c11);
+ callF190(c11);
+ callF209(c11);
+ C12 c12 = new C12();
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ callF133(c12);
+ callF152(c12);
+ callF171(c12);
+ callF190(c12);
+ callF209(c12);
+ callF228(c12);
+ C13 c13 = new C13();
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ callF114(c13);
+ callF133(c13);
+ callF152(c13);
+ callF171(c13);
+ callF190(c13);
+ callF209(c13);
+ callF228(c13);
+ callF247(c13);
+ C14 c14 = new C14();
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ callF95(c14);
+ callF114(c14);
+ callF133(c14);
+ callF152(c14);
+ callF171(c14);
+ callF190(c14);
+ callF209(c14);
+ callF228(c14);
+ callF247(c14);
+ callF266(c14);
+ C15 c15 = new C15();
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ callF76(c15);
+ callF95(c15);
+ callF114(c15);
+ callF133(c15);
+ callF152(c15);
+ callF171(c15);
+ callF190(c15);
+ callF209(c15);
+ callF228(c15);
+ callF247(c15);
+ callF266(c15);
+ callF285(c15);
+ C16 c16 = new C16();
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ callF57(c16);
+ callF76(c16);
+ callF95(c16);
+ callF114(c16);
+ callF133(c16);
+ callF152(c16);
+ callF171(c16);
+ callF190(c16);
+ callF209(c16);
+ callF228(c16);
+ callF247(c16);
+ callF266(c16);
+ callF285(c16);
+ callF304(c16);
+ C17 c17 = new C17();
+ callF0(c17);
+ callF19(c17);
+ callF38(c17);
+ callF57(c17);
+ callF76(c17);
+ callF95(c17);
+ callF114(c17);
+ callF133(c17);
+ callF152(c17);
+ callF171(c17);
+ callF190(c17);
+ callF209(c17);
+ callF228(c17);
+ callF247(c17);
+ callF266(c17);
+ callF285(c17);
+ callF304(c17);
+ callF323(c17);
+ C18 c18 = new C18();
+ callF0(c18);
+ callF19(c18);
+ callF38(c18);
+ callF57(c18);
+ callF76(c18);
+ callF95(c18);
+ callF114(c18);
+ callF133(c18);
+ callF152(c18);
+ callF171(c18);
+ callF190(c18);
+ callF209(c18);
+ callF228(c18);
+ callF247(c18);
+ callF266(c18);
+ callF285(c18);
+ callF304(c18);
+ callF323(c18);
+ callF342(c18);
+ C19 c19 = new C19();
+ callF0(c19);
+ callF19(c19);
+ callF38(c19);
+ callF57(c19);
+ callF76(c19);
+ callF95(c19);
+ callF114(c19);
+ callF133(c19);
+ callF152(c19);
+ callF171(c19);
+ callF190(c19);
+ callF209(c19);
+ callF228(c19);
+ callF247(c19);
+ callF266(c19);
+ callF285(c19);
+ callF304(c19);
+ callF323(c19);
+ callF342(c19);
+ callF361(c19);
+ }
+
+ @Test
+ public void timeConflictDepth01() {
+ C0 c0 = new C0();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth02() {
+ C1 c1 = new C1();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth03() {
+ C2 c2 = new C2();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth04() {
+ C3 c3 = new C3();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth05() {
+ C4 c4 = new C4();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth06() {
+ C5 c5 = new C5();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth07() {
+ C6 c6 = new C6();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth08() {
+ C7 c7 = new C7();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth09() {
+ C8 c8 = new C8();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ callF0(c8);
+ callF19(c8);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth10() {
+ C9 c9 = new C9();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth11() {
+ C10 c10 = new C10();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ callF171(c10);
+ callF190(c10);
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth12() {
+ C11 c11 = new C11();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ callF152(c11);
+ callF171(c11);
+ callF190(c11);
+ callF209(c11);
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth13() {
+ C12 c12 = new C12();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ callF133(c12);
+ callF152(c12);
+ callF171(c12);
+ callF190(c12);
+ callF209(c12);
+ callF228(c12);
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth14() {
+ C13 c13 = new C13();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ callF114(c13);
+ callF133(c13);
+ callF152(c13);
+ callF171(c13);
+ callF190(c13);
+ callF209(c13);
+ callF228(c13);
+ callF247(c13);
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth15() {
+ C14 c14 = new C14();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ callF95(c14);
+ callF114(c14);
+ callF133(c14);
+ callF152(c14);
+ callF171(c14);
+ callF190(c14);
+ callF209(c14);
+ callF228(c14);
+ callF247(c14);
+ callF266(c14);
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth16() {
+ C15 c15 = new C15();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ callF76(c15);
+ callF95(c15);
+ callF114(c15);
+ callF133(c15);
+ callF152(c15);
+ callF171(c15);
+ callF190(c15);
+ callF209(c15);
+ callF228(c15);
+ callF247(c15);
+ callF266(c15);
+ callF285(c15);
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth17() {
+ C16 c16 = new C16();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ callF57(c16);
+ callF76(c16);
+ callF95(c16);
+ callF114(c16);
+ callF133(c16);
+ callF152(c16);
+ callF171(c16);
+ callF190(c16);
+ callF209(c16);
+ callF228(c16);
+ callF247(c16);
+ callF266(c16);
+ callF285(c16);
+ callF304(c16);
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth18() {
+ C17 c17 = new C17();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c17);
+ callF19(c17);
+ callF38(c17);
+ callF57(c17);
+ callF76(c17);
+ callF95(c17);
+ callF114(c17);
+ callF133(c17);
+ callF152(c17);
+ callF171(c17);
+ callF190(c17);
+ callF209(c17);
+ callF228(c17);
+ callF247(c17);
+ callF266(c17);
+ callF285(c17);
+ callF304(c17);
+ callF323(c17);
+ callF0(c17);
+ callF19(c17);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth19() {
+ C18 c18 = new C18();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c18);
+ callF19(c18);
+ callF38(c18);
+ callF57(c18);
+ callF76(c18);
+ callF95(c18);
+ callF114(c18);
+ callF133(c18);
+ callF152(c18);
+ callF171(c18);
+ callF190(c18);
+ callF209(c18);
+ callF228(c18);
+ callF247(c18);
+ callF266(c18);
+ callF285(c18);
+ callF304(c18);
+ callF323(c18);
+ callF342(c18);
+ callF0(c18);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth20() {
+ C19 c19 = new C19();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c19);
+ callF19(c19);
+ callF38(c19);
+ callF57(c19);
+ callF76(c19);
+ callF95(c19);
+ callF114(c19);
+ callF133(c19);
+ callF152(c19);
+ callF171(c19);
+ callF190(c19);
+ callF209(c19);
+ callF228(c19);
+ callF247(c19);
+ callF266(c19);
+ callF285(c19);
+ callF304(c19);
+ callF323(c19);
+ callF342(c19);
+ callF361(c19);
+ }
+ }
+
+ public void callF0(I0 i) {
+ i.f0();
+ }
+
+ public void callF19(I1 i) {
+ i.f19();
+ }
+
+ public void callF38(I2 i) {
+ i.f38();
+ }
+
+ public void callF57(I3 i) {
+ i.f57();
+ }
+
+ public void callF76(I4 i) {
+ i.f76();
+ }
+
+ public void callF95(I5 i) {
+ i.f95();
+ }
+
+ public void callF114(I6 i) {
+ i.f114();
+ }
+
+ public void callF133(I7 i) {
+ i.f133();
+ }
+
+ public void callF152(I8 i) {
+ i.f152();
+ }
+
+ public void callF171(I9 i) {
+ i.f171();
+ }
+
+ public void callF190(I10 i) {
+ i.f190();
+ }
+
+ public void callF209(I11 i) {
+ i.f209();
+ }
+
+ public void callF228(I12 i) {
+ i.f228();
+ }
+
+ public void callF247(I13 i) {
+ i.f247();
+ }
+
+ public void callF266(I14 i) {
+ i.f266();
+ }
+
+ public void callF285(I15 i) {
+ i.f285();
+ }
+
+ public void callF304(I16 i) {
+ i.f304();
+ }
+
+ public void callF323(I17 i) {
+ i.f323();
+ }
+
+ public void callF342(I18 i) {
+ i.f342();
+ }
+
+ public void callF361(I19 i) {
+ i.f361();
+ }
+
+ static class C0 implements I0 {}
+
+ static class C1 implements I0, I1 {}
+
+ static class C2 implements I0, I1, I2 {}
+
+ static class C3 implements I0, I1, I2, I3 {}
+
+ static class C4 implements I0, I1, I2, I3, I4 {}
+
+ static class C5 implements I0, I1, I2, I3, I4, I5 {}
+
+ static class C6 implements I0, I1, I2, I3, I4, I5, I6 {}
+
+ static class C7 implements I0, I1, I2, I3, I4, I5, I6, I7 {}
+
+ static class C8 implements I0, I1, I2, I3, I4, I5, I6, I7, I8 {}
+
+ static class C9 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9 {}
+
+ static class C10 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 {}
+
+ static class C11 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11 {}
+
+ static class C12 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12 {}
+
+ static class C13 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13 {}
+
+ static class C14 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14 {}
+
+ static class C15
+ implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15 {}
+
+ static class C16
+ implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16 {}
+
+ static class C17
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17 {}
+
+ static class C18
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17,
+ I18 {}
+
+ static class C19
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17,
+ I18,
+ I19 {}
+
+ interface I0 {
+ default void f0() {}
+
+ default void f1() {}
+
+ default void f2() {}
+
+ default void f3() {}
+
+ default void f4() {}
+
+ default void f5() {}
+
+ default void f6() {}
+
+ default void f7() {}
+
+ default void f8() {}
+
+ default void f9() {}
+
+ default void f10() {}
+
+ default void f11() {}
+
+ default void f12() {}
+
+ default void f13() {}
+
+ default void f14() {}
+
+ default void f15() {}
+
+ default void f16() {}
+
+ default void f17() {}
+
+ default void f18() {}
+ }
+
+ interface I1 {
+ default void f19() {}
+
+ default void f20() {}
+
+ default void f21() {}
+
+ default void f22() {}
+
+ default void f23() {}
+
+ default void f24() {}
+
+ default void f25() {}
+
+ default void f26() {}
+
+ default void f27() {}
+
+ default void f28() {}
+
+ default void f29() {}
+
+ default void f30() {}
+
+ default void f31() {}
+
+ default void f32() {}
+
+ default void f33() {}
+
+ default void f34() {}
+
+ default void f35() {}
+
+ default void f36() {}
+
+ default void f37() {}
+ }
+
+ interface I2 {
+ default void f38() {}
+
+ default void f39() {}
+
+ default void f40() {}
+
+ default void f41() {}
+
+ default void f42() {}
+
+ default void f43() {}
+
+ default void f44() {}
+
+ default void f45() {}
+
+ default void f46() {}
+
+ default void f47() {}
+
+ default void f48() {}
+
+ default void f49() {}
+
+ default void f50() {}
+
+ default void f51() {}
+
+ default void f52() {}
+
+ default void f53() {}
+
+ default void f54() {}
+
+ default void f55() {}
+
+ default void f56() {}
+ }
+
+ interface I3 {
+ default void f57() {}
+
+ default void f58() {}
+
+ default void f59() {}
+
+ default void f60() {}
+
+ default void f61() {}
+
+ default void f62() {}
+
+ default void f63() {}
+
+ default void f64() {}
+
+ default void f65() {}
+
+ default void f66() {}
+
+ default void f67() {}
+
+ default void f68() {}
+
+ default void f69() {}
+
+ default void f70() {}
+
+ default void f71() {}
+
+ default void f72() {}
+
+ default void f73() {}
+
+ default void f74() {}
+
+ default void f75() {}
+ }
+
+ interface I4 {
+ default void f76() {}
+
+ default void f77() {}
+
+ default void f78() {}
+
+ default void f79() {}
+
+ default void f80() {}
+
+ default void f81() {}
+
+ default void f82() {}
+
+ default void f83() {}
+
+ default void f84() {}
+
+ default void f85() {}
+
+ default void f86() {}
+
+ default void f87() {}
+
+ default void f88() {}
+
+ default void f89() {}
+
+ default void f90() {}
+
+ default void f91() {}
+
+ default void f92() {}
+
+ default void f93() {}
+
+ default void f94() {}
+ }
+
+ interface I5 {
+ default void f95() {}
+
+ default void f96() {}
+
+ default void f97() {}
+
+ default void f98() {}
+
+ default void f99() {}
+
+ default void f100() {}
+
+ default void f101() {}
+
+ default void f102() {}
+
+ default void f103() {}
+
+ default void f104() {}
+
+ default void f105() {}
+
+ default void f106() {}
+
+ default void f107() {}
+
+ default void f108() {}
+
+ default void f109() {}
+
+ default void f110() {}
+
+ default void f111() {}
+
+ default void f112() {}
+
+ default void f113() {}
+ }
+
+ interface I6 {
+ default void f114() {}
+
+ default void f115() {}
+
+ default void f116() {}
+
+ default void f117() {}
+
+ default void f118() {}
+
+ default void f119() {}
+
+ default void f120() {}
+
+ default void f121() {}
+
+ default void f122() {}
+
+ default void f123() {}
+
+ default void f124() {}
+
+ default void f125() {}
+
+ default void f126() {}
+
+ default void f127() {}
+
+ default void f128() {}
+
+ default void f129() {}
+
+ default void f130() {}
+
+ default void f131() {}
+
+ default void f132() {}
+ }
+
+ interface I7 {
+ default void f133() {}
+
+ default void f134() {}
+
+ default void f135() {}
+
+ default void f136() {}
+
+ default void f137() {}
+
+ default void f138() {}
+
+ default void f139() {}
+
+ default void f140() {}
+
+ default void f141() {}
+
+ default void f142() {}
+
+ default void f143() {}
+
+ default void f144() {}
+
+ default void f145() {}
+
+ default void f146() {}
+
+ default void f147() {}
+
+ default void f148() {}
+
+ default void f149() {}
+
+ default void f150() {}
+
+ default void f151() {}
+ }
+
+ interface I8 {
+ default void f152() {}
+
+ default void f153() {}
+
+ default void f154() {}
+
+ default void f155() {}
+
+ default void f156() {}
+
+ default void f157() {}
+
+ default void f158() {}
+
+ default void f159() {}
+
+ default void f160() {}
+
+ default void f161() {}
+
+ default void f162() {}
+
+ default void f163() {}
+
+ default void f164() {}
+
+ default void f165() {}
+
+ default void f166() {}
+
+ default void f167() {}
+
+ default void f168() {}
+
+ default void f169() {}
+
+ default void f170() {}
+ }
+
+ interface I9 {
+ default void f171() {}
+
+ default void f172() {}
+
+ default void f173() {}
+
+ default void f174() {}
+
+ default void f175() {}
+
+ default void f176() {}
+
+ default void f177() {}
+
+ default void f178() {}
+
+ default void f179() {}
+
+ default void f180() {}
+
+ default void f181() {}
+
+ default void f182() {}
+
+ default void f183() {}
+
+ default void f184() {}
+
+ default void f185() {}
+
+ default void f186() {}
+
+ default void f187() {}
+
+ default void f188() {}
+
+ default void f189() {}
+ }
+
+ interface I10 {
+ default void f190() {}
+
+ default void f191() {}
+
+ default void f192() {}
+
+ default void f193() {}
+
+ default void f194() {}
+
+ default void f195() {}
+
+ default void f196() {}
+
+ default void f197() {}
+
+ default void f198() {}
+
+ default void f199() {}
+
+ default void f200() {}
+
+ default void f201() {}
+
+ default void f202() {}
+
+ default void f203() {}
+
+ default void f204() {}
+
+ default void f205() {}
+
+ default void f206() {}
+
+ default void f207() {}
+
+ default void f208() {}
+ }
+
+ interface I11 {
+ default void f209() {}
+
+ default void f210() {}
+
+ default void f211() {}
+
+ default void f212() {}
+
+ default void f213() {}
+
+ default void f214() {}
+
+ default void f215() {}
+
+ default void f216() {}
+
+ default void f217() {}
+
+ default void f218() {}
+
+ default void f219() {}
+
+ default void f220() {}
+
+ default void f221() {}
+
+ default void f222() {}
+
+ default void f223() {}
+
+ default void f224() {}
+
+ default void f225() {}
+
+ default void f226() {}
+
+ default void f227() {}
+ }
+
+ interface I12 {
+ default void f228() {}
+
+ default void f229() {}
+
+ default void f230() {}
+
+ default void f231() {}
+
+ default void f232() {}
+
+ default void f233() {}
+
+ default void f234() {}
+
+ default void f235() {}
+
+ default void f236() {}
+
+ default void f237() {}
+
+ default void f238() {}
+
+ default void f239() {}
+
+ default void f240() {}
+
+ default void f241() {}
+
+ default void f242() {}
+
+ default void f243() {}
+
+ default void f244() {}
+
+ default void f245() {}
+
+ default void f246() {}
+ }
+
+ interface I13 {
+ default void f247() {}
+
+ default void f248() {}
+
+ default void f249() {}
+
+ default void f250() {}
+
+ default void f251() {}
+
+ default void f252() {}
+
+ default void f253() {}
+
+ default void f254() {}
+
+ default void f255() {}
+
+ default void f256() {}
+
+ default void f257() {}
+
+ default void f258() {}
+
+ default void f259() {}
+
+ default void f260() {}
+
+ default void f261() {}
+
+ default void f262() {}
+
+ default void f263() {}
+
+ default void f264() {}
+
+ default void f265() {}
+ }
+
+ interface I14 {
+ default void f266() {}
+
+ default void f267() {}
+
+ default void f268() {}
+
+ default void f269() {}
+
+ default void f270() {}
+
+ default void f271() {}
+
+ default void f272() {}
+
+ default void f273() {}
+
+ default void f274() {}
+
+ default void f275() {}
+
+ default void f276() {}
+
+ default void f277() {}
+
+ default void f278() {}
+
+ default void f279() {}
+
+ default void f280() {}
+
+ default void f281() {}
+
+ default void f282() {}
+
+ default void f283() {}
+
+ default void f284() {}
+ }
+
+ interface I15 {
+ default void f285() {}
+
+ default void f286() {}
+
+ default void f287() {}
+
+ default void f288() {}
+
+ default void f289() {}
+
+ default void f290() {}
+
+ default void f291() {}
+
+ default void f292() {}
+
+ default void f293() {}
+
+ default void f294() {}
+
+ default void f295() {}
+
+ default void f296() {}
+
+ default void f297() {}
+
+ default void f298() {}
+
+ default void f299() {}
+
+ default void f300() {}
+
+ default void f301() {}
+
+ default void f302() {}
+
+ default void f303() {}
+ }
+
+ interface I16 {
+ default void f304() {}
+
+ default void f305() {}
+
+ default void f306() {}
+
+ default void f307() {}
+
+ default void f308() {}
+
+ default void f309() {}
+
+ default void f310() {}
+
+ default void f311() {}
+
+ default void f312() {}
+
+ default void f313() {}
+
+ default void f314() {}
+
+ default void f315() {}
+
+ default void f316() {}
+
+ default void f317() {}
+
+ default void f318() {}
+
+ default void f319() {}
+
+ default void f320() {}
+
+ default void f321() {}
+
+ default void f322() {}
+ }
+
+ interface I17 {
+ default void f323() {}
+
+ default void f324() {}
+
+ default void f325() {}
+
+ default void f326() {}
+
+ default void f327() {}
+
+ default void f328() {}
+
+ default void f329() {}
+
+ default void f330() {}
+
+ default void f331() {}
+
+ default void f332() {}
+
+ default void f333() {}
+
+ default void f334() {}
+
+ default void f335() {}
+
+ default void f336() {}
+
+ default void f337() {}
+
+ default void f338() {}
+
+ default void f339() {}
+
+ default void f340() {}
+
+ default void f341() {}
+ }
+
+ interface I18 {
+ default void f342() {}
+
+ default void f343() {}
+
+ default void f344() {}
+
+ default void f345() {}
+
+ default void f346() {}
+
+ default void f347() {}
+
+ default void f348() {}
+
+ default void f349() {}
+
+ default void f350() {}
+
+ default void f351() {}
+
+ default void f352() {}
+
+ default void f353() {}
+
+ default void f354() {}
+
+ default void f355() {}
+
+ default void f356() {}
+
+ default void f357() {}
+
+ default void f358() {}
+
+ default void f359() {}
+
+ default void f360() {}
+ }
+
+ interface I19 {
+ default void f361() {}
+
+ default void f362() {}
+
+ default void f363() {}
+
+ default void f364() {}
+
+ default void f365() {}
+
+ default void f366() {}
+
+ default void f367() {}
+
+ default void f368() {}
+
+ default void f369() {}
+
+ default void f370() {}
+
+ default void f371() {}
+
+ default void f372() {}
+
+ default void f373() {}
+
+ default void f374() {}
+
+ default void f375() {}
+
+ default void f376() {}
+
+ default void f377() {}
+
+ default void f378() {}
+
+ default void f379() {}
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
new file mode 100755
index 000000000000..eea3b84a4498
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import sys
+
+max_conflict_depth = 20 # In practice does not go above 20 for reasonable IMT sizes
+try:
+ imt_size = int(sys.argv[1])
+except (IndexError, ValueError):
+ print("Usage: python ImtConflictPerfTestGen.py <IMT_SIZE>")
+ sys.exit(1)
+
+license = """\
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+"""
+description = """
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py.
+ * It measures the performance impact of conflicts in interface method tables.
+ * Run `python ImtConflictPerfTestGen.py > ImtConflictPerfTest.java` to regenerate.
+ *
+ * Each interface has 64 methods, which is the current size of an IMT. C0 implements
+ * one interface, C1 implements two, C2 implements three, and so on. The intent
+ * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in
+ * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by
+ * the fact that we hash interface methods by taking their method index modulo 64.
+ * (Note that a "conflict depth" of 1 means no conflict at all.)
+ */\
+"""
+
+print(license)
+print("package android.libcore;")
+imports = """
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+"""
+print(imports)
+print(description)
+
+print("@RunWith(AndroidJUnit4.class)")
+print("@LargeTest")
+print("public class ImtConflictPerfTest {")
+print(" @Rule")
+print(" public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();")
+print("")
+# Warm up interface method tables
+print(" @Before")
+print(" public void setup() {")
+for i in range(max_conflict_depth):
+ print(" C{0} c{0} = new C{0}();".format(i))
+ for j in range(i+1):
+ print(" callF{}(c{});".format(imt_size * j, i))
+print(" }")
+
+# Print test cases--one for each conflict depth
+for i in range(max_conflict_depth):
+ print(" @Test")
+ print(" public void timeConflictDepth{:02d}() {{".format(i+1))
+ print(" C{0} c{0} = new C{0}();".format(i))
+ print(" BenchmarkState state = mPerfStatusReporter.getBenchmarkState();")
+ print(" while (state.keepRunning()) {")
+ # Cycle through each interface method in an IMT entry in order
+ # to test all conflict resolution possibilities
+ for j in range(max_conflict_depth):
+ print(" callF{}(c{});".format(imt_size * (j % (i + 1)), i))
+ print(" }")
+ print(" }")
+
+# Make calls through the IMTs
+for i in range(max_conflict_depth):
+ print(" public void callF{0}(I{1} i) {{ i.f{0}(); }}".format(imt_size*i, i))
+
+# Class definitions, implementing varying amounts of interfaces
+for i in range(max_conflict_depth):
+ interfaces = ", ".join(["I{}".format(j) for j in range(i+1)])
+ print(" static class C{} implements {} {{}}".format(i, interfaces))
+
+# Interface definitions, each with enough methods to fill an entire IMT
+for i in range(max_conflict_depth):
+ print(" interface I{} {{".format(i))
+ for j in range(imt_size):
+ print(" default void f{}() {{}}".format(i*imt_size + j))
+ print(" }")
+
+print("}") \ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
new file mode 100644
index 000000000000..ca9977974a8a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Compares various kinds of method invocation. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MethodInvocationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ interface I {
+ void emptyInterface();
+ }
+
+ static class C implements I {
+ private int mField;
+
+ private int getField() {
+ return mField;
+ }
+
+ public void timeInternalGetter(BenchmarkState state) {
+ int result = 0;
+ while (state.keepRunning()) {
+ result = getField();
+ }
+ }
+
+ public void timeInternalFieldAccess(BenchmarkState state) {
+ int result = 0;
+ while (state.keepRunning()) {
+ result = mField;
+ }
+ }
+
+ public static void emptyStatic() {}
+
+ public void emptyVirtual() {}
+
+ public void emptyInterface() {}
+ }
+
+ public void timeInternalGetter() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ new C().timeInternalGetter(state);
+ }
+
+ public void timeInternalFieldAccess() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ new C().timeInternalFieldAccess(state);
+ }
+
+ // Test an intrinsic.
+ @Test
+ public void timeStringLength() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = "hello, world!".length();
+ }
+ }
+
+ @Test
+ public void timeEmptyStatic() {
+ C c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyStatic();
+ }
+ }
+
+ @Test
+ public void timeEmptyVirtual() {
+ C c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyVirtual();
+ }
+ }
+
+ @Test
+ public void timeEmptyInterface() {
+ I c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyInterface();
+ }
+ }
+
+ public static class Inner {
+ private int mI;
+
+ private void privateMethod() {
+ ++mI;
+ }
+
+ protected void protectedMethod() {
+ ++mI;
+ }
+
+ public void publicMethod() {
+ ++mI;
+ }
+
+ void packageMethod() {
+ ++mI;
+ }
+
+ final void finalPackageMethod() {
+ ++mI;
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPublicMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.publicMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerProtectedMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.protectedMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPrivateMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.privateMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPackageMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.packageMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerFinalPackageMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.finalPackageMethod();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
new file mode 100644
index 000000000000..8496fbecb6bd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How much do various kinds of multiplication cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MultiplicationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeMultiplyIntByConstant10() {
+ int result = 1;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= 10;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByConstant8() {
+ int result = 1;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= 8;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByVariable10() {
+ int result = 1;
+ int factor = 10;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= factor;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByVariable8() {
+ int result = 1;
+ int factor = 8;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= factor;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
new file mode 100644
index 000000000000..bb794249e9f4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferenceGetPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ boolean mIntrinsicDisabled;
+
+ private Object mObj = "str";
+
+ @Before
+ public void setUp() throws Exception {
+ Field intrinsicDisabledField = Reference.class.getDeclaredField("disableIntrinsic");
+ intrinsicDisabledField.setAccessible(true);
+ intrinsicDisabledField.setBoolean(null, mIntrinsicDisabled);
+ }
+
+ @Test
+ public void timeSoftReferenceGet() throws Exception {
+ Reference soft = new SoftReference(mObj);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = soft.get();
+ }
+ }
+
+ @Test
+ public void timeWeakReferenceGet() throws Exception {
+ Reference weak = new WeakReference(mObj);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = weak.get();
+ }
+ }
+
+ @Test
+ public void timeNonPreservedWeakReferenceGet() throws Exception {
+ Reference weak = new WeakReference(mObj);
+ mObj = null;
+ Runtime.getRuntime().gc();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = weak.get();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
new file mode 100644
index 000000000000..2ef68ca7bdb2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Benchmark to evaluate the performance of References. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferencePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Object mObject;
+
+ // How fast can references can be allocated?
+ @Test
+ public void timeAlloc() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ new PhantomReference(mObject, queue);
+ }
+ }
+
+ // How fast can references can be allocated and manually enqueued?
+ @Test
+ public void timeAllocAndEnqueue() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ }
+ }
+
+ // How fast can references can be allocated, enqueued, and polled?
+ @Test
+ public void timeAllocEnqueueAndPoll() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ queue.poll();
+ }
+ }
+
+ // How fast can references can be allocated, enqueued, and removed?
+ @Test
+ public void timeAllocEnqueueAndRemove() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ try {
+ queue.remove();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ private static class FinalizableObject {
+ AtomicInteger mCount;
+
+ FinalizableObject(AtomicInteger count) {
+ this.mCount = count;
+ }
+
+ @Override
+ protected void finalize() {
+ mCount.incrementAndGet();
+ }
+ }
+
+ // How fast does finalization run?
+ @Test
+ public void timeFinalization() {
+ // Allocate a bunch of finalizable objects.
+ int n = 0;
+ AtomicInteger count = new AtomicInteger(0);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ n++;
+ new FinalizableObject(count);
+ }
+
+ // Run GC so the objects will be collected for finalization.
+ Runtime.getRuntime().gc();
+
+ // Wait for finalization.
+ Runtime.getRuntime().runFinalization();
+
+ // Double check all the objects were finalized.
+ int got = count.get();
+ if (n != got) {
+ throw new IllegalStateException(
+ String.format("Only %i of %i objects finalized?", got, n));
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
new file mode 100644
index 000000000000..65a2fdbae304
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This measures performance of operations on small BigIntegers. We manually determine the number of
+ * iterations so that it should cause total memory allocation on the order of a few hundred
+ * megabytes. Due to BigInteger's reliance on finalization, these may unfortunately all be kept
+ * around at once.
+ *
+ * <p>This is not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SmallBigIntegerPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ // We allocate about 2 1/3 BigIntegers per iteration.
+ // Assuming 100 bytes/BigInteger, this gives us around 500MB total.
+ static final BigInteger BIG_THREE = BigInteger.valueOf(3);
+ static final BigInteger BIG_FOUR = BigInteger.valueOf(4);
+
+ @Test
+ public void testSmallBigInteger() {
+ final Random r = new Random();
+ BigInteger x = new BigInteger(20, r);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ // We know this converges, but the compiler doesn't.
+ if (x.and(BigInteger.ONE).equals(BigInteger.ONE)) {
+ x = x.multiply(BIG_THREE).add(BigInteger.ONE);
+ } else {
+ x = x.shiftRight(1);
+ }
+ }
+ if (x.signum() < 0 || x.compareTo(BIG_FOUR) > 0) {
+ throw new AssertionError("Something went horribly wrong.");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
new file mode 100644
index 000000000000..4f5c54d6a847
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How long does it take to access a string in the dex cache? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringDexCachePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeStringDexCacheAccess() {
+ int v = 0;
+ int count = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ // Deliberately obscured to make optimizations less likely.
+ String s = (count >= 0) ? "hello, world!" : null;
+ v += s.length();
+ ++count;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
new file mode 100644
index 000000000000..08ad92694013
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How do the various schemes for iterating through a string compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringIterationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeStringIteration0() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ for (int i = 0; i < s.length(); ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+
+ @Test
+ public void timeStringIteration1() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ for (int i = 0, length = s.length(); i < length; ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+
+ @Test
+ public void timeStringIteration2() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ char[] chars = s.toCharArray();
+ for (int i = 0, length = chars.length; i < length; ++i) {
+ ch = chars[i];
+ }
+ }
+ }
+
+ @Test
+ public void timeStringToCharArray() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char[] chars = s.toCharArray();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
new file mode 100644
index 000000000000..5aacfc25bfd1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class SystemArrayCopyPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "arrayLength={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[][] {
+ {2}, {4}, {8}, {16}, {32}, {64}, {128}, {256}, {512}, {1024}, {2048}, {4096},
+ {8192}, {16384}, {32768}, {65536}, {131072}, {262144}
+ });
+ }
+
+ @Parameterized.Parameter(0)
+ public int arrayLength;
+
+ // Provides benchmarking for different types of arrays using the arraycopy function.
+ @Test
+ public void timeSystemCharArrayCopy() {
+ final int len = arrayLength;
+ char[] src = new char[len];
+ char[] dst = new char[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemByteArrayCopy() {
+ final int len = arrayLength;
+ byte[] src = new byte[len];
+ byte[] dst = new byte[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemShortArrayCopy() {
+ final int len = arrayLength;
+ short[] src = new short[len];
+ short[] dst = new short[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemIntArrayCopy() {
+ final int len = arrayLength;
+ int[] src = new int[len];
+ int[] dst = new int[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemLongArrayCopy() {
+ final int len = arrayLength;
+ long[] src = new long[len];
+ long[] dst = new long[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemFloatArrayCopy() {
+ final int len = arrayLength;
+ float[] src = new float[len];
+ float[] dst = new float[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemDoubleArrayCopy() {
+ final int len = arrayLength;
+ double[] src = new double[len];
+ double[] dst = new double[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemBooleanArrayCopy() {
+ final int len = arrayLength;
+ boolean[] src = new boolean[len];
+ boolean[] dst = new boolean[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
new file mode 100644
index 000000000000..7e71976fdc9d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Is there a performance reason to "Prefer virtual over interface", as the Android documentation
+ * once claimed?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class VirtualVersusInterfacePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeMapPut() {
+ Map<String, String> map = new HashMap<String, String>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.put("hello", "world");
+ }
+ }
+
+ @Test
+ public void timeHashMapPut() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.put("hello", "world");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
new file mode 100644
index 000000000000..eec0734cffda
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.CharArrayWriter;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class XmlSerializePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "mDatasetAsString({0}), mSeed({1})")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[][] {
+ {"0.99 0.7 0.7 0.7 0.7 0.7", 854328},
+ {"0.999 0.3 0.3 0.95 0.9 0.9", 854328},
+ {"0.99 0.7 0.7 0.7 0.7 0.7", 312547},
+ {"0.999 0.3 0.3 0.95 0.9 0.9", 312547}
+ });
+ }
+
+ @Parameterized.Parameter(0)
+ public String mDatasetAsString;
+
+ @Parameterized.Parameter(1)
+ public int mSeed;
+
+ double[] mDataset;
+ private Constructor<? extends XmlSerializer> mKxmlConstructor;
+ private Constructor<? extends XmlSerializer> mFastConstructor;
+
+ private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long mSeed)
+ throws Exception {
+ double contChance = mDataset[0];
+ double levelUpChance = mDataset[1];
+ double levelDownChance = mDataset[2];
+ double attributeChance = mDataset[3];
+ double writeChance1 = mDataset[4];
+ double writeChance2 = mDataset[5];
+
+ XmlSerializer serializer = (XmlSerializer) ctor.newInstance();
+
+ CharArrayWriter w = new CharArrayWriter();
+ serializer.setOutput(w);
+ int level = 0;
+ Random r = new Random(mSeed);
+ char[] toWrite = {'a', 'b', 'c', 'd', 's', 'z'};
+ serializer.startDocument("UTF-8", true);
+ while (r.nextDouble() < contChance) {
+ while (level > 0 && r.nextDouble() < levelUpChance) {
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ }
+ while (r.nextDouble() < levelDownChance) {
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ }
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ while (r.nextDouble() < attributeChance) {
+ serializer.attribute("aaaaaa", "cccccc", "dddddd");
+ }
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ while (r.nextDouble() < writeChance1) serializer.text(toWrite, 0, 5);
+ while (r.nextDouble() < writeChance2) serializer.text("Textxtsxtxtxt ");
+ }
+ serializer.endDocument();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setUp() throws Exception {
+ mKxmlConstructor =
+ (Constructor)
+ Class.forName("com.android.org.kxml2.io.KXmlSerializer").getConstructor();
+ mFastConstructor =
+ (Constructor)
+ Class.forName("com.android.internal.util.FastXmlSerializer")
+ .getConstructor();
+ String[] splitStrings = mDatasetAsString.split(" ");
+ mDataset = new double[splitStrings.length];
+ for (int i = 0; i < splitStrings.length; i++) {
+ mDataset[i] = Double.parseDouble(splitStrings[i]);
+ }
+ }
+
+ private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor)
+ throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ serializeRandomXml(ctor, mSeed);
+ }
+ }
+
+ @Test
+ public void timeKxml() throws Exception {
+ internalTimeSerializer(mKxmlConstructor);
+ }
+
+ @Test
+ public void timeFast() throws Exception {
+ internalTimeSerializer(mFastConstructor);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
new file mode 100644
index 000000000000..517e3ce39d7e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFilePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private File mFile;
+
+ @Parameters(name = "numEntries={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{128}, {1024}, {8192}});
+ }
+
+ @Parameterized.Parameter(0)
+ public int numEntries;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile(getClass().getName(), ".zip");
+ mFile.deleteOnExit();
+ writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), numEntries, 0);
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ }
+ zipFile.close();
+ }
+
+ @Test
+ public void timeZipFileOpen() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ZipFile zf = new ZipFile(mFile);
+ }
+ }
+
+ /** Compresses the given number of files, each of the given size, into a .zip archive. */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ ze.setSize(entrySize);
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
new file mode 100644
index 000000000000..faa96285cefd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFileReadPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "readBufferSize={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}});
+ }
+
+ private File mFile;
+
+ @Parameterized.Parameter(0)
+ public int readBufferSize;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile(getClass().getName(), ".zip");
+ writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), 2, 1024 * 1024);
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ }
+ zipFile.close();
+ }
+
+ /** Compresses the given number of files, each of the given size, into a .zip archive. */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ ze.setSize(entrySize);
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+
+ @Test
+ public void timeZipFileRead() throws Exception {
+ byte[] readBuffer = new byte[readBufferSize];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ InputStream is = zipFile.getInputStream(zipEntry);
+ while (true) {
+ if (is.read(readBuffer, 0, readBuffer.length) < 0) {
+ break;
+ }
+ }
+ }
+ zipFile.close();
+ }
+ }
+}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 38500aff34ea..f6ae56f01758 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -507,6 +507,22 @@ public class BlobStoreManager {
}
/**
+ * Release all the leases which are currently held by the caller.
+ *
+ * @hide
+ */
+ public void releaseAllLeases() throws Exception {
+ try {
+ mService.releaseAllLeases(mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the remaining quota size for acquiring a lease (in bytes) which indicates the
* remaining amount of data that an app can acquire a lease on before the System starts
* rejecting lease requests.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index 39a9fb4bb1f4..1566fa8b00d0 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -31,6 +31,7 @@ interface IBlobStoreManager {
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
long leaseTimeoutMillis, in String packageName);
void releaseLease(in BlobHandle handle, in String packageName);
+ void releaseAllLeases(in String packageName);
long getRemainingLeaseQuotaBytes(String packageName);
void waitForIdle(in RemoteCallback callback);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index c83ca8c2e31d..9ac3e412b1e4 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -554,6 +554,21 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ private void releaseAllLeasesInternal(int callingUid, String callingPackage) {
+ synchronized (mBlobsLock) {
+ // Remove the package from the leasee list
+ mBlobsMap.forEach((blobHandle, blobMetadata) -> {
+ blobMetadata.removeLeasee(callingPackage, callingUid);
+ });
+ writeBlobsInfoAsync();
+
+ if (LOGV) {
+ Slog.v(TAG, "Release all leases associated with pkg="
+ + callingPackage + ", uid=" + callingUid);
+ }
+ }
+ }
+
private long getRemainingLeaseQuotaBytesInternal(int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
final long remainingQuota = BlobStoreConfig.getAppDataBytesLimit()
@@ -1376,7 +1391,7 @@ public class BlobStoreManagerService extends SystemService {
}
}
- private boolean isAllowedBlobAccess(int uid, String packageName) {
+ private boolean isAllowedBlobStoreAccess(int uid, String packageName) {
return (!Process.isSdkSandboxUid(uid) && !Process.isIsolated(uid)
&& !mPackageManagerInternal.isInstantApp(packageName, UserHandle.getUserId(uid)));
}
@@ -1442,7 +1457,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (!isAllowedBlobAccess(callingUid, packageName)) {
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to create session; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1491,7 +1506,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (!isAllowedBlobAccess(callingUid, packageName)) {
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1522,7 +1537,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (!isAllowedBlobAccess(callingUid, packageName)) {
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1546,7 +1561,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (!isAllowedBlobAccess(callingUid, packageName)) {
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1555,6 +1570,21 @@ public class BlobStoreManagerService extends SystemService {
}
@Override
+ public void releaseAllLeases(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
+ releaseAllLeasesInternal(callingUid, packageName);
+ }
+
+ @Override
public long getRemainingLeaseQuotaBytes(@NonNull String packageName) {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -1629,7 +1659,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (!isAllowedBlobAccess(callingUid, packageName)) {
+ if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index c0a81487fe62..f4faec85002a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -28,8 +28,8 @@ import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
import static com.android.server.tare.EconomicPolicy.eventToString;
import static com.android.server.tare.EconomicPolicy.getEventType;
import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.cakeToString;
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -161,7 +161,7 @@ class Agent {
@GuardedBy("mLock")
private boolean isAffordableLocked(long balance, long price, long ctp) {
- return balance >= price && mScribe.getRemainingConsumableNarcsLocked() >= ctp;
+ return balance >= price && mScribe.getRemainingConsumableCakesLocked() >= ctp;
}
@GuardedBy("mLock")
@@ -464,13 +464,13 @@ class Agent {
+ eventToString(transaction.eventId)
+ (transaction.tag == null ? "" : ":" + transaction.tag)
+ " for " + appToString(userId, pkgName)
- + " by " + narcToString(transaction.delta - newDelta));
+ + " by " + cakeToString(transaction.delta - newDelta));
transaction = new Ledger.Transaction(
transaction.startTimeMs, transaction.endTimeMs,
transaction.eventId, transaction.tag, newDelta, transaction.ctp);
}
ledger.recordTransaction(transaction);
- mScribe.adjustRemainingConsumableNarcsLocked(-transaction.ctp);
+ mScribe.adjustRemainingConsumableCakesLocked(-transaction.ctp);
if (transaction.delta != 0 && notifyOnAffordabilityChange) {
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
@@ -724,7 +724,7 @@ class Agent {
private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() != 0) {
- mScribe.adjustRemainingConsumableNarcsLocked(-ledger.getCurrentBalance());
+ mScribe.adjustRemainingConsumableCakesLocked(-ledger.getCurrentBalance());
}
mScribe.discardLedgerLocked(userId, pkgName);
mCurrentOngoingEvents.delete(userId, pkgName);
@@ -872,7 +872,7 @@ class Agent {
return;
}
mTrendCalculator.reset(getBalanceLocked(userId, pkgName),
- mScribe.getRemainingConsumableNarcsLocked(),
+ mScribe.getRemainingConsumableCakesLocked(),
mActionAffordabilityNotes.get(userId, pkgName));
ongoingEvents.forEach(mTrendCalculator);
final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
@@ -1260,11 +1260,11 @@ class Agent {
pw.print(" runtime=");
TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
pw.print(" delta/sec=");
- pw.print(narcToString(ongoingEvent.getDeltaPerSec()));
+ pw.print(cakeToString(ongoingEvent.getDeltaPerSec()));
final long ctp = ongoingEvent.getCtpPerSec();
if (ctp != 0) {
pw.print(" ctp/sec=");
- pw.print(narcToString(ongoingEvent.getCtpPerSec()));
+ pw.print(cakeToString(ongoingEvent.getCtpPerSec()));
}
pw.print(" refCount=");
pw.print(ongoingEvent.refCount);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index 71e00cf053e2..c2e81882eed2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -97,8 +97,8 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
-import static com.android.server.tare.TareUtils.arcToNarc;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.arcToCake;
+import static com.android.server.tare.TareUtils.cakeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -219,143 +219,143 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceExempted = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
+ mMinSatiatedBalanceExempted = arcToCake(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED));
- mMinSatiatedBalanceOther = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
+ mMinSatiatedBalanceOther = arcToCake(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
- mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
+ mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
DEFAULT_AM_MAX_SATIATED_BALANCE));
- mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt(
KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT));
mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT)));
- final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
+ final long exactAllowWhileIdleWakeupBasePrice = arcToCake(
mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE));
mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP)),
exactAllowWhileIdleWakeupBasePrice));
mActions.put(ACTION_ALARM_WAKEUP_EXACT,
new Action(ACTION_ALARM_WAKEUP_EXACT,
- arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
+ arcToCake(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP)),
- arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE))));
final long inexactAllowWhileIdleWakeupBasePrice =
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP)),
inexactAllowWhileIdleWakeupBasePrice));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
new Action(ACTION_ALARM_WAKEUP_INEXACT,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE))));
final long exactAllowWhileIdleNonWakeupBasePrice =
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP)),
exactAllowWhileIdleNonWakeupBasePrice));
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
new Action(ACTION_ALARM_NONWAKEUP_EXACT,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE))));
final long inexactAllowWhileIdleNonWakeupBasePrice =
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP)),
inexactAllowWhileIdleNonWakeupBasePrice));
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE))));
mActions.put(ACTION_ALARM_CLOCK,
new Action(ACTION_ALARM_CLOCK,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE))));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT)),
- (long) (arcToNarc(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
+ (long) (arcToCake(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING)),
- arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX))));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT)),
- arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING)),
- arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX))));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX))));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX))));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- arcToNarc(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
+ arcToCake(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX))));
}
@@ -364,14 +364,14 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
void dump(IndentingPrintWriter pw) {
pw.println("Min satiated balances:");
pw.increaseIndent();
- pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
- pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+ pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
+ pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
- pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
+ pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
pw.print("Consumption limits: [");
- pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
pw.print(", ");
- pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.print(cakeToString(mHardSatiatedConsumptionLimit));
pw.println("]");
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 1e480155f18a..3a26aae217c2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -21,7 +21,7 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.cakeToString;
import android.annotation.CallSuper;
import android.annotation.IntDef;
@@ -203,7 +203,7 @@ public abstract class EconomicPolicy {
abstract long getMaxSatiatedBalance();
/**
- * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * Returns the maximum number of cakes that should be consumed during a full 100% discharge
* cycle. This is the initial limit. The system may choose to increase the limit over time,
* but the increased limit should never exceed the value returned from
* {@link #getHardSatiatedConsumptionLimit()}.
@@ -211,7 +211,7 @@ public abstract class EconomicPolicy {
abstract long getInitialSatiatedConsumptionLimit();
/**
- * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+ * Returns the maximum number of cakes that should be consumed during a full 100% discharge
* cycle. This is the hard limit that should never be exceeded.
*/
abstract long getHardSatiatedConsumptionLimit();
@@ -430,9 +430,9 @@ public abstract class EconomicPolicy {
pw.print(actionToString(action.id));
pw.print(": ");
pw.print("ctp=");
- pw.print(narcToString(action.costToProduce));
+ pw.print(cakeToString(action.costToProduce));
pw.print(", basePrice=");
- pw.print(narcToString(action.basePrice));
+ pw.print(cakeToString(action.basePrice));
pw.println();
}
@@ -440,11 +440,11 @@ public abstract class EconomicPolicy {
pw.print(rewardToString(reward.id));
pw.print(": ");
pw.print("instant=");
- pw.print(narcToString(reward.instantReward));
+ pw.print(cakeToString(reward.instantReward));
pw.print(", ongoing/sec=");
- pw.print(narcToString(reward.ongoingRewardPerSecond));
+ pw.print(cakeToString(reward.ongoingRewardPerSecond));
pw.print(", maxDaily=");
- pw.print(narcToString(reward.maxDailyReward));
+ pw.print(cakeToString(reward.maxDailyReward));
pw.println();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index c93480700ca7..ce4604f80f50 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -23,8 +23,8 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.cakeToString;
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -528,9 +528,9 @@ public class InternalResourceService extends SystemService {
void maybePerformQuantitativeEasingLocked() {
// We don't need to increase the limit if the device runs out of consumable credits
// when the battery is low.
- final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+ final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
- || remainingConsumableNarcs > 0) {
+ || remainingConsumableCakes > 0) {
return;
}
final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
@@ -539,8 +539,8 @@ public class InternalResourceService extends SystemService {
final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
if (newConsumptionLimit != currentConsumptionLimit) {
- Slog.i(TAG, "Increasing consumption limit from " + narcToString(currentConsumptionLimit)
- + " to " + narcToString(newConsumptionLimit));
+ Slog.i(TAG, "Increasing consumption limit from " + cakeToString(currentConsumptionLimit)
+ + " to " + cakeToString(newConsumptionLimit));
mScribe.setConsumptionLimitLocked(newConsumptionLimit);
adjustCreditSupplyLocked(/* allowIncrease */ true);
}
@@ -562,16 +562,16 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private void adjustCreditSupplyLocked(boolean allowIncrease) {
final long newLimit = getConsumptionLimitLocked();
- final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
- if (remainingConsumableNarcs == newLimit) {
+ final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
+ if (remainingConsumableCakes == newLimit) {
return;
}
- if (remainingConsumableNarcs > newLimit) {
- mScribe.adjustRemainingConsumableNarcsLocked(newLimit - remainingConsumableNarcs);
+ if (remainingConsumableCakes > newLimit) {
+ mScribe.adjustRemainingConsumableCakesLocked(newLimit - remainingConsumableCakes);
} else if (allowIncrease) {
final double perc = mCurrentBatteryLevel / 100d;
- final long shortfall = newLimit - remainingConsumableNarcs;
- mScribe.adjustRemainingConsumableNarcsLocked((long) (perc * shortfall));
+ final long shortfall = newLimit - remainingConsumableCakes;
+ mScribe.adjustRemainingConsumableCakesLocked((long) (perc * shortfall));
}
mAgent.onCreditSupplyChanged();
}
@@ -919,7 +919,7 @@ public class InternalResourceService extends SystemService {
+ cost.price * (action.ongoingDurationMs / 1000);
}
return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
- && mScribe.getRemainingConsumableNarcsLocked() >= requiredBalance;
+ && mScribe.getRemainingConsumableCakesLocked() >= requiredBalance;
}
}
@@ -947,7 +947,7 @@ public class InternalResourceService extends SystemService {
}
final long minBalance = Math.min(
mAgent.getBalanceLocked(userId, pkgName),
- mScribe.getRemainingConsumableNarcsLocked());
+ mScribe.getRemainingConsumableCakesLocked());
return minBalance * 1000 / totalCostPerSecond;
}
}
@@ -1103,21 +1103,21 @@ public class InternalResourceService extends SystemService {
final long consumptionLimit = getConsumptionLimitLocked();
pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
- pw.print(narcToString(consumptionLimit));
+ pw.print(cakeToString(consumptionLimit));
pw.print("/");
- pw.print(narcToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
+ pw.print(cakeToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
pw.print("/");
- pw.println(narcToString(mScribe.getSatiatedConsumptionLimitLocked()));
+ pw.println(cakeToString(mScribe.getSatiatedConsumptionLimitLocked()));
- final long remainingConsumable = mScribe.getRemainingConsumableNarcsLocked();
+ final long remainingConsumable = mScribe.getRemainingConsumableCakesLocked();
pw.print("Goods remaining: ");
- pw.print(narcToString(remainingConsumable));
+ pw.print(cakeToString(remainingConsumable));
pw.print(" (");
pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
pw.println("% of current limit)");
pw.print("Device wealth: ");
- pw.println(narcToString(mScribe.getNarcsInCirculationForLoggingLocked()));
+ pw.println(cakeToString(mScribe.getCakesInCirculationForLoggingLocked()));
pw.println();
pw.print("Exempted apps", mExemptedApps);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 0eddd2223d23..99b93ce5c22c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -106,8 +106,8 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
-import static com.android.server.tare.TareUtils.arcToNarc;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.arcToCake;
+import static com.android.server.tare.TareUtils.cakeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -221,116 +221,116 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceExempted = arcToNarc(
+ mMinSatiatedBalanceExempted = arcToCake(
mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED));
- mMinSatiatedBalanceOther = arcToNarc(
+ mMinSatiatedBalanceOther = arcToCake(
mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
- mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
+ mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
DEFAULT_JS_MAX_SATIATED_BALANCE));
- mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+ mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt(
KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT));
mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT)));
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
DEFAULT_JS_ACTION_JOB_MAX_START_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE))));
mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE))));
mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_START_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE))));
mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE))));
mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE))));
mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE))));
mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_CTP,
DEFAULT_JS_ACTION_JOB_LOW_START_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE))));
mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE))));
mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_CTP,
DEFAULT_JS_ACTION_JOB_MIN_START_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE))));
mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE))));
mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP)),
- arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
+ arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE))));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT)),
- (long) (arcToNarc(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
+ (long) (arcToCake(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING)),
- arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX))));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT)),
- arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING)),
- arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX))));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX))));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX))));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- arcToNarc(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
+ arcToCake(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING)),
- arcToNarc(mParser.getInt(
+ arcToCake(mParser.getInt(
KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX))));
}
@@ -339,14 +339,14 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
void dump(IndentingPrintWriter pw) {
pw.println("Min satiated balances:");
pw.increaseIndent();
- pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
- pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+ pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
+ pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
- pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
+ pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
pw.print("Consumption limits: [");
- pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+ pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
pw.print(", ");
- pw.print(narcToString(mHardSatiatedConsumptionLimit));
+ pw.print(cakeToString(mHardSatiatedConsumptionLimit));
pw.println("]");
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index dfdc20a32999..2e2a9b56d3df 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -18,9 +18,9 @@ package com.android.server.tare;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static com.android.server.tare.TareUtils.cakeToString;
import static com.android.server.tare.TareUtils.dumpTime;
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -129,7 +129,7 @@ class Ledger {
}
void dump(IndentingPrintWriter pw, int numRecentTransactions) {
- pw.print("Current balance", narcToString(getCurrentBalance())).println();
+ pw.print("Current balance", cakeToString(getCurrentBalance())).println();
final int size = mTransactions.size();
for (int i = Math.max(0, size - numRecentTransactions); i < size; ++i) {
@@ -146,9 +146,9 @@ class Ledger {
pw.print(")");
}
pw.print(" --> ");
- pw.print(narcToString(transaction.delta));
+ pw.print(cakeToString(transaction.delta));
pw.print(" (ctp=");
- pw.print(narcToString(transaction.ctp));
+ pw.print(cakeToString(transaction.ctp));
pw.println(")");
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md
index 33eadffb8592..72d506972dd1 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/README.md
+++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md
@@ -105,5 +105,7 @@ consumed.
* ARC: Android Resource Credits are the "currency" units used as an abstraction layer over the real
battery drain. They allow the system to standardize costs and prices across various devices.
+* Cake: A lie; also the smallest unit of an ARC (1 cake = one-billionth of an ARC = 1 nano-ARC).
+ When the apps request to do something, we shall let them eat cake.
* NARC: The smallest unit of an ARC. A narc is 1 nano-ARC.
* Satiated: used to refer to when the device is fully charged (at 100% battery level) \ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 866211024169..7442877548f1 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -84,7 +84,7 @@ public class Scribe {
private static final String XML_ATTR_USER_ID = "userId";
private static final String XML_ATTR_VERSION = "version";
private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
- private static final String XML_ATTR_REMAINING_CONSUMABLE_NARCS = "remainingConsumableNarcs";
+ private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes";
private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
/** Version of the file schema. */
@@ -100,7 +100,7 @@ public class Scribe {
@GuardedBy("mIrs.getLock()")
private long mSatiatedConsumptionLimit;
@GuardedBy("mIrs.getLock()")
- private long mRemainingConsumableNarcs;
+ private long mRemainingConsumableCakes;
@GuardedBy("mIrs.getLock()")
private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
@@ -122,10 +122,10 @@ public class Scribe {
}
@GuardedBy("mIrs.getLock()")
- void adjustRemainingConsumableNarcsLocked(long delta) {
+ void adjustRemainingConsumableCakesLocked(long delta) {
if (delta != 0) {
// No point doing any work if the change is 0.
- mRemainingConsumableNarcs += delta;
+ mRemainingConsumableCakes += delta;
postWrite();
}
}
@@ -168,7 +168,7 @@ public class Scribe {
* call it for normal operation.
*/
@GuardedBy("mIrs.getLock()")
- long getNarcsInCirculationForLoggingLocked() {
+ long getCakesInCirculationForLoggingLocked() {
long sum = 0;
for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
@@ -178,10 +178,10 @@ public class Scribe {
return sum;
}
- /** Returns the total amount of narcs that remain to be consumed. */
+ /** Returns the total amount of cakes that remain to be consumed. */
@GuardedBy("mIrs.getLock()")
- long getRemainingConsumableNarcsLocked() {
- return mRemainingConsumableNarcs;
+ long getRemainingConsumableCakesLocked() {
+ return mRemainingConsumableCakes;
}
@GuardedBy("mIrs.getLock()")
@@ -189,11 +189,11 @@ public class Scribe {
mLedgers.clear();
if (!recordExists()) {
mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked();
- mRemainingConsumableNarcs = mIrs.getConsumptionLimitLocked();
+ mRemainingConsumableCakes = mIrs.getConsumptionLimitLocked();
return;
}
mSatiatedConsumptionLimit = 0;
- mRemainingConsumableNarcs = 0;
+ mRemainingConsumableCakes = 0;
final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
@@ -254,8 +254,8 @@ public class Scribe {
parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
mIrs.getInitialSatiatedConsumptionLimitLocked());
final long consumptionLimit = mIrs.getConsumptionLimitLocked();
- mRemainingConsumableNarcs = Math.min(consumptionLimit,
- parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+ mRemainingConsumableCakes = Math.min(consumptionLimit,
+ parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
consumptionLimit));
break;
case XML_TAG_USER:
@@ -285,11 +285,11 @@ public class Scribe {
@GuardedBy("mIrs.getLock()")
void setConsumptionLimitLocked(long limit) {
- if (mRemainingConsumableNarcs > limit) {
- mRemainingConsumableNarcs = limit;
+ if (mRemainingConsumableCakes > limit) {
+ mRemainingConsumableCakes = limit;
} else if (limit > mSatiatedConsumptionLimit) {
- final long diff = mSatiatedConsumptionLimit - mRemainingConsumableNarcs;
- mRemainingConsumableNarcs = (limit - diff);
+ final long diff = mSatiatedConsumptionLimit - mRemainingConsumableCakes;
+ mRemainingConsumableCakes = (limit - diff);
}
mSatiatedConsumptionLimit = limit;
postWrite();
@@ -306,7 +306,7 @@ public class Scribe {
TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
mLedgers.clear();
- mRemainingConsumableNarcs = 0;
+ mRemainingConsumableCakes = 0;
mSatiatedConsumptionLimit = 0;
mLastReclamationTime = 0;
}
@@ -491,8 +491,8 @@ public class Scribe {
out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
- out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
- mRemainingConsumableNarcs);
+ out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
+ mRemainingConsumableCakes);
out.endTag(null, XML_TAG_HIGH_LEVEL_STATE);
for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
index 78508d4129ef..87db8637ddac 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -26,7 +26,7 @@ import java.text.SimpleDateFormat;
import java.time.Clock;
class TareUtils {
- private static final long NARC_IN_ARC = 1_000_000_000L;
+ private static final long CAKE_IN_ARC = 1_000_000_000L;
@SuppressLint("SimpleDateFormat")
private static final SimpleDateFormat sDumpDateFormat =
@@ -35,8 +35,8 @@ class TareUtils {
@VisibleForTesting
static Clock sSystemClock = Clock.systemUTC();
- static long arcToNarc(int arcs) {
- return arcs * NARC_IN_ARC;
+ static long arcToCake(int arcs) {
+ return arcs * CAKE_IN_ARC;
}
static void dumpTime(IndentingPrintWriter pw, long time) {
@@ -47,26 +47,26 @@ class TareUtils {
return sSystemClock.millis();
}
- static int narcToArc(long narcs) {
- return (int) (narcs / NARC_IN_ARC);
+ static int cakeToArc(long cakes) {
+ return (int) (cakes / CAKE_IN_ARC);
}
@NonNull
- static String narcToString(long narcs) {
- if (narcs == 0) {
+ static String cakeToString(long cakes) {
+ if (cakes == 0) {
return "0 ARCs";
}
- final long sub = Math.abs(narcs) % NARC_IN_ARC;
- final long arcs = narcToArc(narcs);
+ final long sub = Math.abs(cakes) % CAKE_IN_ARC;
+ final long arcs = cakeToArc(cakes);
if (arcs == 0) {
return sub == 1
- ? sub + " narc"
- : sub + " narcs";
+ ? sub + " cake"
+ : sub + " cakes";
}
StringBuilder sb = new StringBuilder();
sb.append(arcs);
if (sub > 0) {
- sb.append(".").append(sub / (NARC_IN_ARC / 1000));
+ sb.append(".").append(sub / (CAKE_IN_ARC / 1000));
}
sb.append(" ARC");
if (arcs != 1 || sub > 0) {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 968be3e20791..fb68c6d84d6a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -577,8 +577,8 @@ status_t BootAnimation::readyToRun() {
mDisplay = display;
mContext = context;
mSurface = surface;
- mWidth = w;
- mHeight = h;
+ mInitWidth = mWidth = w;
+ mInitHeight = mHeight = h;
mFlingerSurfaceControl = control;
mFlingerSurface = s;
mTargetInset = -1;
@@ -611,6 +611,7 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(mDisplay, mSurface);
+ mFlingerSurfaceControl->updateDefaultBufferSize(newWidth, newHeight);
const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
mWidth = limitedSize.width;
mHeight = limitedSize.height;
@@ -1515,8 +1516,10 @@ bool BootAnimation::playAnimation(const Animation& animation) {
processDisplayEvents();
- const int animationX = (mWidth - animation.width) / 2;
- const int animationY = (mHeight - animation.height) / 2;
+ const double ratio_w = static_cast<double>(mWidth) / mInitWidth;
+ const double ratio_h = static_cast<double>(mHeight) / mInitHeight;
+ const int animationX = (mWidth - animation.width * ratio_w) / 2;
+ const int animationY = (mHeight - animation.height * ratio_h) / 2;
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
@@ -1532,12 +1535,16 @@ bool BootAnimation::playAnimation(const Animation& animation) {
initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);
}
- const int xc = animationX + frame.trimX;
- const int yc = animationY + frame.trimY;
+ const int trimWidth = frame.trimWidth * ratio_w;
+ const int trimHeight = frame.trimHeight * ratio_h;
+ const int trimX = frame.trimX * ratio_w;
+ const int trimY = frame.trimY * ratio_h;
+ const int xc = animationX + trimX;
+ const int yc = animationY + trimY;
glClear(GL_COLOR_BUFFER_BIT);
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
- const int frameDrawY = mHeight - (yc + frame.trimHeight);
+ const int frameDrawY = mHeight - (yc + trimHeight);
float fade = 0;
// if the part hasn't been stopped yet then continue fading if necessary
@@ -1554,7 +1561,7 @@ bool BootAnimation::playAnimation(const Animation& animation) {
glUniform1f(mImageColorProgressLocation, colorProgress);
}
glEnable(GL_BLEND);
- drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight);
+ drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight);
glDisable(GL_BLEND);
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index a136ad0a90f2..86582053b4e1 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -213,6 +213,8 @@ private:
Texture mAndroid[2];
int mWidth;
int mHeight;
+ int mInitWidth;
+ int mInitHeight;
int mMaxWidth = 0;
int mMaxHeight = 0;
int mCurrentInset;
diff --git a/core/api/current.txt b/core/api/current.txt
index e260ad046918..c8a43db2f9c2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -44254,6 +44254,9 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
public final class ImsReasonInfo implements android.os.Parcelable {
@@ -44475,10 +44478,10 @@ package android.telephony.ims {
method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback);
}
- public static class ProvisioningManager.FeatureProvisioningCallback {
+ public abstract static class ProvisioningManager.FeatureProvisioningCallback {
ctor public ProvisioningManager.FeatureProvisioningCallback();
- method public void onFeatureProvisioningChanged(int, int, boolean);
- method public void onRcsFeatureProvisioningChanged(int, int, boolean);
+ method public abstract void onFeatureProvisioningChanged(int, int, boolean);
+ method public abstract void onRcsFeatureProvisioningChanged(int, int, boolean);
}
public class RcsUceAdapter {
@@ -44521,15 +44524,6 @@ package android.telephony.ims.feature {
field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
}
- public class RcsFeature {
- }
-
- public static class RcsFeature.RcsImsCapabilities {
- field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
- field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
- }
-
}
package android.telephony.ims.stub {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b25d1e302da5..ec4ad8b704c3 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -15024,7 +15024,7 @@ package android.telephony.ims {
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+ field @Deprecated public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
@@ -15307,6 +15307,9 @@ package android.telephony.ims.feature {
method public void addCapabilities(int);
method public boolean isCapable(int);
method public void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f4a12a5a6a0f..167cd344a2c9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2403,8 +2403,6 @@ package android.service.dreams {
public abstract class DreamOverlayService extends android.app.Service {
ctor public DreamOverlayService();
- method @Nullable public final CharSequence getDreamLabel();
- method public final boolean isPreviewMode();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
method public final void requestExit();
@@ -3387,6 +3385,7 @@ package android.window {
public final class WindowContainerTransaction implements android.os.Parcelable {
ctor public WindowContainerTransaction();
+ method @NonNull public android.window.WindowContainerTransaction clearLaunchAdjacentFlagRoot(@NonNull android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams);
method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken);
method public int describeContents();
@@ -3405,6 +3404,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction setErrorCallbackToken(@NonNull android.os.IBinder);
method @NonNull public android.window.WindowContainerTransaction setFocusable(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction setLaunchAdjacentFlagRoot(@NonNull android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]);
method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int);
method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int);
diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java
new file mode 100644
index 000000000000..ecf449d7936a
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSession.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+interface AccessibilityInputMethodSession {
+ void finishInput();
+
+ void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd);
+
+ void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection,
+ int sessionId);
+
+ void setEnabled(boolean enabled);
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java
new file mode 100644
index 000000000000..3252ab23486f
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityInputMethodSessionWrapper.java
@@ -0,0 +1,116 @@
+/*
+ * 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.accessibilityservice;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+final class AccessibilityInputMethodSessionWrapper extends IAccessibilityInputMethodSession.Stub {
+ private final Handler mHandler;
+
+ @NonNull
+ private final AtomicReference<AccessibilityInputMethodSession> mSessionRef;
+
+ AccessibilityInputMethodSessionWrapper(
+ @NonNull Looper looper, @NonNull AccessibilityInputMethodSession session) {
+ mSessionRef = new AtomicReference<>(session);
+ mHandler = Handler.createAsync(looper);
+ }
+
+ @AnyThread
+ @Nullable
+ AccessibilityInputMethodSession getSession() {
+ return mSessionRef.get();
+ }
+
+ @Override
+ public void updateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
+ candidatesEnd);
+ } else {
+ mHandler.post(() -> doUpdateSelection(oldSelStart, oldSelEnd, newSelStart,
+ newSelEnd, candidatesStart, candidatesEnd));
+ }
+ }
+
+ private void doUpdateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.updateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
+ candidatesEnd);
+ }
+ }
+
+ @Override
+ public void finishInput() {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doFinishInput();
+ } else {
+ mHandler.post(this::doFinishInput);
+ }
+ }
+
+ private void doFinishInput() {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.finishInput();
+ }
+ }
+
+ @Override
+ public void finishSession() {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doFinishSession();
+ } else {
+ mHandler.post(this::doFinishSession);
+ }
+ }
+
+ private void doFinishSession() {
+ mSessionRef.set(null);
+ }
+
+ @Override
+ public void invalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ doInvalidateInput(editorInfo, connection, sessionId);
+ } else {
+ mHandler.post(() -> doInvalidateInput(editorInfo, connection, sessionId));
+ }
+ }
+
+ private void doInvalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ final AccessibilityInputMethodSession session = mSessionRef.get();
+ if (session != null) {
+ session.invalidateInput(editorInfo, connection, sessionId);
+ }
+ }
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3cb04e710d3b..c17fbf19516b 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -40,8 +40,6 @@ import android.graphics.ParcelableColorSpace;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
-import android.inputmethodservice.IInputMethodSessionWrapper;
-import android.inputmethodservice.RemoteInputConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -68,22 +66,19 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodSession;
import com.android.internal.inputmethod.CancellationGroup;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -639,20 +634,10 @@ public abstract class AccessibilityService extends Service {
/** This is called when the system action list is changed. */
void onSystemActionsChanged();
/** This is called when an app requests ime sessions or when the service is enabled. */
- void createImeSession(IInputSessionWithIdCallback callback);
- /**
- * This is called when InputMethodManagerService requests to set the session enabled or
- * disabled
- */
- void setImeSessionEnabled(InputMethodSession session, boolean enabled);
- /** This is called when an app binds input or when the service is enabled. */
- void bindInput(InputBinding binding);
- /** This is called when an app unbinds input or when the service is disabled. */
- void unbindInput();
+ void createImeSession(IAccessibilityInputMethodSessionCallback callback);
/** This is called when an app starts input or when the service is enabled. */
- void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken);
+ void startInput(@Nullable RemoteAccessibilityInputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting);
}
/**
@@ -2740,42 +2725,20 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
if (mInputMethod != null) {
mInputMethod.createImeSession(callback);
}
}
@Override
- public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
- if (mInputMethod != null) {
- mInputMethod.setImeSessionEnabled(session, enabled);
- }
- }
-
- @Override
- public void bindInput(InputBinding binding) {
- if (mInputMethod != null) {
- mInputMethod.bindInput(binding);
- }
- }
-
- @Override
- public void unbindInput() {
- if (mInputMethod != null) {
- mInputMethod.unbindInput();
- }
- }
-
- @Override
- public void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ public void startInput(@Nullable RemoteAccessibilityInputConnection connection,
+ @NonNull EditorInfo editorInfo, boolean restarting) {
if (mInputMethod != null) {
if (restarting) {
- mInputMethod.restartInput(inputConnection, editorInfo);
+ mInputMethod.restartInput(connection, editorInfo);
} else {
- mInputMethod.startInput(inputConnection, editorInfo);
+ mInputMethod.startInput(connection, editorInfo);
}
}
}
@@ -2806,8 +2769,6 @@ public abstract class AccessibilityService extends Service {
private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
private static final int DO_CREATE_IME_SESSION = 15;
private static final int DO_SET_IME_SESSION_ENABLED = 16;
- private static final int DO_BIND_INPUT = 17;
- private static final int DO_UNBIND_INPUT = 18;
private static final int DO_START_INPUT = 19;
private final HandlerCaller mCaller;
@@ -2818,15 +2779,14 @@ public abstract class AccessibilityService extends Service {
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
/**
- * This is not {@null} only between {@link #bindInput(InputBinding)} and
- * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if
- * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary
- * blocking operations.
+ * This is not {@code null} only between {@link #bindInput()} and {@link #unbindInput()} so
+ * that {@link RemoteAccessibilityInputConnection} can query if {@link #unbindInput()} has
+ * already been called or not, mainly to avoid unnecessary blocking operations.
*
* <p>This field must be set and cleared only from the binder thread(s), where the system
- * guarantees that {@link #bindInput(InputBinding)},
- * {@link #startInput(IBinder, IInputContext, EditorInfo, boolean)}, and
- * {@link #unbindInput()} are called with the same order as the original calls
+ * guarantees that {@link #bindInput()},
+ * {@link #startInput(IRemoteAccessibilityInputConnection, EditorInfo, boolean)},
+ * and {@link #unbindInput()} are called with the same order as the original calls
* in {@link com.android.server.inputmethod.InputMethodManagerService}.
* See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p>
*/
@@ -2927,7 +2887,7 @@ public abstract class AccessibilityService extends Service {
}
/** This is called when an app requests ime sessions or when the service is enabled. */
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
mCaller.sendMessage(message);
}
@@ -2936,10 +2896,11 @@ public abstract class AccessibilityService extends Service {
* This is called when InputMethodManagerService requests to set the session enabled or
* disabled
*/
- public void setImeSessionEnabled(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabled(IAccessibilityInputMethodSession session,
+ boolean enabled) {
try {
- InputMethodSession ls = ((IInputMethodSessionWrapper)
- session).getInternalInputMethodSession();
+ AccessibilityInputMethodSession ls =
+ ((AccessibilityInputMethodSessionWrapper) session).getSession();
if (ls == null) {
Log.w(LOG_TAG, "Session is already finished: " + session);
return;
@@ -2952,17 +2913,11 @@ public abstract class AccessibilityService extends Service {
}
/** This is called when an app binds input or when the service is enabled. */
- public void bindInput(InputBinding binding) {
+ public void bindInput() {
if (mCancellationGroup != null) {
Log.e(LOG_TAG, "bindInput must be paired with unbindInput.");
}
mCancellationGroup = new CancellationGroup();
- InputConnection ic = new RemoteInputConnection(new WeakReference<>(() -> mContext),
- IInputContext.Stub.asInterface(binding.getConnectionToken()),
- mCancellationGroup);
- InputBinding nu = new InputBinding(ic, binding);
- final Message message = mCaller.obtainMessageO(DO_BIND_INPUT, nu);
- mCaller.sendMessage(message);
}
/** This is called when an app unbinds input or when the service is disabled. */
@@ -2974,18 +2929,17 @@ public abstract class AccessibilityService extends Service {
} else {
Log.e(LOG_TAG, "unbindInput must be paired with bindInput.");
}
- mCaller.sendMessage(mCaller.obtainMessage(DO_UNBIND_INPUT));
}
/** This is called when an app starts input or when the service is enabled. */
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
if (mCancellationGroup == null) {
Log.e(LOG_TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
- final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, editorInfo, mCancellationGroup, restarting ? 1 : 0,
+ final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */,
+ connection, editorInfo, mCancellationGroup, restarting ? 1 : 0,
0 /* unused */);
mCaller.sendMessage(message);
}
@@ -3157,44 +3111,33 @@ public abstract class AccessibilityService extends Service {
}
case DO_CREATE_IME_SESSION: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- IInputSessionWithIdCallback callback =
- (IInputSessionWithIdCallback) message.obj;
+ IAccessibilityInputMethodSessionCallback callback =
+ (IAccessibilityInputMethodSessionCallback) message.obj;
mCallback.createImeSession(callback);
}
return;
}
case DO_SET_IME_SESSION_ENABLED: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.setImeSessionEnabled((InputMethodSession) message.obj,
- message.arg1 != 0);
- }
- return;
- }
- case DO_BIND_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.bindInput((InputBinding) message.obj);
- }
- return;
- }
- case DO_UNBIND_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.unbindInput();
+ AccessibilityInputMethodSession session =
+ (AccessibilityInputMethodSession) message.obj;
+ session.setEnabled(message.arg1 != 0);
}
return;
}
case DO_START_INPUT: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final SomeArgs args = (SomeArgs) message.obj;
- final IBinder startInputToken = (IBinder) args.arg1;
- final IInputContext inputContext = (IInputContext) args.arg2;
+ final IRemoteAccessibilityInputConnection connection =
+ (IRemoteAccessibilityInputConnection) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
- final InputConnection ic = inputContext != null
- ? new RemoteInputConnection(new WeakReference<>(() -> mContext),
- inputContext, cancellationGroup) : null;
+ final RemoteAccessibilityInputConnection ic = connection == null ? null
+ : new RemoteAccessibilityInputConnection(
+ connection, cancellationGroup);
info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
- mCallback.startInput(ic, info, restarting, startInputToken);
+ mCallback.startInput(ic, info, restarting);
args.recycle();
}
return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 94da61f82d29..3bc61e560d8c 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -25,10 +25,9 @@ import android.accessibilityservice.MagnificationConfig;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
/**
* Top-level interface to an accessibility service component.
@@ -69,14 +68,14 @@ import com.android.internal.view.IInputSessionWithIdCallback;
void onSystemActionsChanged();
- void createImeSession(IInputSessionWithIdCallback callback);
+ void createImeSession(in IAccessibilityInputMethodSessionCallback callback);
- void setImeSessionEnabled(IInputMethodSession session, boolean enabled);
+ void setImeSessionEnabled(in IAccessibilityInputMethodSession session, boolean enabled);
- void bindInput(in InputBinding binding);
+ void bindInput();
void unbindInput();
- void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo editorInfo, boolean restarting);
+ void startInput(in IRemoteAccessibilityInputConnection connection, in EditorInfo editorInfo,
+ boolean restarting);
}
diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java
index 79bac9bce620..1585f99759cd 100644
--- a/core/java/android/accessibilityservice/InputMethod.java
+++ b/core/java/android/accessibilityservice/InputMethod.java
@@ -18,36 +18,23 @@ package android.accessibilityservice;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.graphics.Rect;
-import android.inputmethodservice.IInputMethodSessionWrapper;
-import android.inputmethodservice.RemoteInputConnection;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputSessionWithIdCallback;
-
-import java.util.concurrent.Executor;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
/**
* This class provides input method APIs. Some public methods such as
@@ -61,9 +48,8 @@ public class InputMethod {
private static final String LOG_TAG = "A11yInputMethod";
private final AccessibilityService mService;
- private InputBinding mInputBinding;
private boolean mInputStarted;
- private InputConnection mStartedInputConnection;
+ private RemoteAccessibilityInputConnection mStartedInputConnection;
private EditorInfo mInputEditorInfo;
/**
@@ -131,9 +117,7 @@ public class InputMethod {
* to perform whatever behavior you would like.
*/
public void onFinishInput() {
- if (mStartedInputConnection != null) {
- mStartedInputConnection.finishComposingText();
- }
+ // Intentionally empty
}
/**
@@ -152,41 +136,26 @@ public class InputMethod {
// Intentionally empty
}
- final void createImeSession(IInputSessionWithIdCallback callback) {
- InputMethodSession session = onCreateInputMethodSessionInterface();
+ final void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
+ final AccessibilityInputMethodSessionWrapper wrapper =
+ new AccessibilityInputMethodSessionWrapper(mService.getMainLooper(),
+ new SessionImpl());
try {
- IInputMethodSessionWrapper wrap =
- new IInputMethodSessionWrapper(mService, session, null);
- callback.sessionCreated(wrap, mService.getConnectionId());
+ callback.sessionCreated(wrapper, mService.getConnectionId());
} catch (RemoteException ignored) {
}
}
- final void setImeSessionEnabled(@NonNull InputMethodSession session, boolean enabled) {
- ((InputMethodSessionForAccessibility) session).setEnabled(enabled);
- }
-
- final void bindInput(@NonNull InputBinding binding) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.bindInput");
- mInputBinding = binding;
- Log.v(LOG_TAG, "bindInput(): binding=" + binding);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- final void unbindInput() {
- Log.v(LOG_TAG, "unbindInput(): binding=" + mInputBinding);
- // Unbind input is per process per display.
- mInputBinding = null;
- }
-
- final void startInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ final void startInput(@Nullable RemoteAccessibilityInputConnection ic,
+ @NonNull EditorInfo attribute) {
Log.v(LOG_TAG, "startInput(): editor=" + attribute);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.startInput");
doStartInput(ic, attribute, false /* restarting */);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- final void restartInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ final void restartInput(@Nullable RemoteAccessibilityInputConnection ic,
+ @NonNull EditorInfo attribute) {
Log.v(LOG_TAG, "restartInput(): editor=" + attribute);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.restartInput");
doStartInput(ic, attribute, true /* restarting */);
@@ -194,7 +163,8 @@ public class InputMethod {
}
- final void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+ final void doStartInput(RemoteAccessibilityInputConnection ic, EditorInfo attribute,
+ boolean restarting) {
if ((ic == null || !restarting) && mInputStarted) {
doFinishInput();
if (ic == null) {
@@ -220,17 +190,13 @@ public class InputMethod {
mInputEditorInfo = null;
}
- private InputMethodSession onCreateInputMethodSessionInterface() {
- return new InputMethodSessionForAccessibility();
- }
-
/**
* This class provides the allowed list of {@link InputConnection} APIs for
* accessibility services.
*/
public final class AccessibilityInputConnection {
- private InputConnection mIc;
- AccessibilityInputConnection(InputConnection ic) {
+ private final RemoteAccessibilityInputConnection mIc;
+ AccessibilityInputConnection(RemoteAccessibilityInputConnection ic) {
this.mIc = ic;
}
@@ -249,7 +215,7 @@ public class InputMethod {
* int, int)} on the current accessibility service after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the accessibility service by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.</p>
*
@@ -282,7 +248,7 @@ public class InputMethod {
* int,int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.</p>
*
@@ -367,9 +333,8 @@ public class InputMethod {
* delete only half of a surrogate pair. Also take care not to
* delete more characters than are in the editor, as that may have
* ill effects on the application. Calling this method will cause
- * the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
- * int, int)} on your service after the batch input is over.</p>
+ * the editor to call {@link InputMethod#onUpdateSelection(int, int, int, int, int, int)}
+ * on your service after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -381,7 +346,7 @@ public class InputMethod {
* indices to the size of the contents to avoid crashes. Since
* this changes the contents of the editor, you need to make the
* changes known to the input method by calling
- * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)},
* but be careful to wait until the batch edit is over if one is
* in progress.</p>
*
@@ -522,12 +487,13 @@ public class InputMethod {
}
/**
- * Concrete implementation of InputMethodSession that provides all of the standard behavior
- * for an input method session.
+ * Concrete implementation of {@link AccessibilityInputMethodSession} that provides all of the
+ * standard behavior for an A11y input method session.
*/
- private final class InputMethodSessionForAccessibility implements InputMethodSession {
+ private final class SessionImpl implements AccessibilityInputMethodSession {
boolean mEnabled = true;
+ @Override
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
@@ -549,86 +515,15 @@ public class InputMethod {
}
@Override
- public void viewClicked(boolean focusChanged) {
- }
-
- @Override
- public void updateCursor(@NonNull Rect newCursor) {
- }
-
- @Override
- public void displayCompletions(
- @SuppressLint("ArrayReturn") @NonNull CompletionInfo[] completions) {
- }
-
- @Override
- public void updateExtractedText(int token, @NonNull ExtractedText text) {
- }
-
- public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
- @NonNull EventCallback callback) {
- }
-
- public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
- @NonNull EventCallback callback) {
- }
-
- public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
- @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
- }
-
- @Override
- public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
- @NonNull EventCallback callback) {
- }
-
- @Override
- public void appPrivateCommand(@NonNull String action, @NonNull Bundle data) {
- }
-
- @Override
- public void toggleSoftInput(int showFlags, int hideFlags) {
- }
-
- @Override
- public void updateCursorAnchorInfo(@NonNull CursorAnchorInfo cursorAnchorInfo) {
- }
-
- @Override
- public void notifyImeHidden() {
- }
-
- @Override
- public void removeImeSurface() {
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext,
- int sessionId) {
- if (mStartedInputConnection instanceof RemoteInputConnection) {
- final RemoteInputConnection ric =
- (RemoteInputConnection) mStartedInputConnection;
- if (!ric.isSameConnection(inputContext)) {
- // This is not an error, and can be safely ignored.
- return;
- }
- editorInfo.makeCompatible(
- mService.getApplicationInfo().targetSdkVersion);
- restartInput(new RemoteInputConnection(ric, sessionId), editorInfo);
+ public void invalidateInput(EditorInfo editorInfo,
+ IRemoteAccessibilityInputConnection connection, int sessionId) {
+ if (!mStartedInputConnection.isSameConnection(connection)) {
+ // This is not an error, and can be safely ignored.
+ return;
}
+ editorInfo.makeCompatible(mService.getApplicationInfo().targetSdkVersion);
+ restartInput(new RemoteAccessibilityInputConnection(mStartedInputConnection, sessionId),
+ editorInfo);
}
}
}
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 7b7b1efdb86b..668dc6b8ec63 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -454,7 +454,13 @@ public class ActivityClient {
}
}
- /** Removes the snapshot of home task. */
+ /**
+ * Removes the outdated snapshot of the home task.
+ *
+ * @param homeToken The token of the home task, or null if you have the
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS} permission and
+ * want us to find the home task token for you.
+ */
public void invalidateHomeTaskSnapshot(IBinder homeToken) {
try {
getActivityClientController().invalidateHomeTaskSnapshot(homeToken);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7d68eb92f206..b4cabada0522 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7030,7 +7030,13 @@ public final class ActivityThread extends ClientTransactionHandler
// local, we'll need to wait for the publishing of the provider.
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
- key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+ if (key.mHolder != null) {
+ if (DEBUG_PROVIDER) {
+ Slog.i(TAG, "already received provider: " + auth);
+ }
+ } else {
+ key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+ }
holder = key.mHolder;
}
if (holder != null && holder.provider == null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 56d655c9cc6f..53e7559b910b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -361,9 +361,9 @@ public class Notification implements Parcelable
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
* that you take care of task management as described in the
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
- * Stack</a> document. In particular, make sure to read the notification section
- * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
- * Notifications</a> for the correct ways to launch an application from a
+ * Stack</a> document. In particular, make sure to read the
+ * <a href="{@docRoot}/training/notify-user/navigation">Start
+ * an Activity from a Notification</a> page for the correct ways to launch an application from a
* notification.
*/
public PendingIntent contentIntent;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 893dc2f6ace4..ac6759396c8f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -64,13 +64,11 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodSession;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputSessionWithIdCallback;
import libcore.io.IoUtils;
@@ -1574,26 +1572,14 @@ public final class UiAutomation {
}
@Override
- public void createImeSession(IInputSessionWithIdCallback callback) {
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
/* do nothing */
}
@Override
- public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
- }
-
- @Override
- public void bindInput(InputBinding binding) {
- }
-
- @Override
- public void unbindInput() {
- }
-
- @Override
- public void startInput(@Nullable InputConnection inputConnection,
- @NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ public void startInput(
+ @Nullable RemoteAccessibilityInputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting) {
}
@Override
diff --git a/core/java/android/app/trust/TEST_MAPPING b/core/java/android/app/trust/TEST_MAPPING
index b9c46bfbba8c..23923eeb83ee 100644
--- a/core/java/android/app/trust/TEST_MAPPING
+++ b/core/java/android/app/trust/TEST_MAPPING
@@ -11,5 +11,18 @@
}
]
}
+ ],
+ "trust-tablet": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
} \ No newline at end of file
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index c4ac483fd10e..da4ecdd8c1f2 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -172,17 +172,20 @@ public abstract class AbstractThreadedSyncAdapter {
}
private class ISyncAdapterImpl extends ISyncAdapter.Stub {
- private void enforceCallerSystem() {
+ private boolean isCallerSystem() {
final long callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID) {
android.util.EventLog.writeEvent(0x534e4554, "203229608", -1, "");
- return;
+ return false;
}
+ return true;
}
@Override
public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) {
- enforceCallerSystem();
+ if (!isCallerSystem()) {
+ return;
+ }
Handler.getMain().sendMessage(obtainMessage(
AbstractThreadedSyncAdapter::handleOnUnsyncableAccount,
AbstractThreadedSyncAdapter.this, cb));
@@ -191,13 +194,15 @@ public abstract class AbstractThreadedSyncAdapter {
@Override
public void startSync(ISyncContext syncContext, String authority, Account account,
Bundle extras) {
+ if (!isCallerSystem()) {
+ return;
+ }
if (ENABLE_LOG) {
if (extras != null) {
extras.size(); // Unparcel so its toString() will show the contents.
}
Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras);
}
- enforceCallerSystem();
try {
final SyncContext syncContextClient = new SyncContext(syncContext);
@@ -254,7 +259,9 @@ public abstract class AbstractThreadedSyncAdapter {
@Override
public void cancelSync(ISyncContext syncContext) {
- enforceCallerSystem();
+ if (!isCallerSystem()) {
+ return;
+ }
try {
// synchronize to make sure that mSyncThreads doesn't change between when we
// check it and when we use it
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0d258ce6c0fc..4d56c1dcaa80 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -486,7 +486,7 @@ public abstract class Context {
/**
* @hide Flag for {@link #bindService}: For only the case where the binding
- * is coming from the system, set the process state to FOREGROUND_SERVICE
+ * is coming from the system, set the process state to BOUND_FOREGROUND_SERVICE
* instead of the normal maximum of IMPORTANT_FOREGROUND. That is, this is
* saying that the process shouldn't participate in the normal power reduction
* modes (removing network access etc).
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 660368aeca14..1c9713df972a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,7 +1,8 @@
# Remain no owner because multiple modules may touch this file.
per-file Context.java = *
per-file ContextWrapper.java = *
-per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
per-file Intent.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ca7d77b2c44a..54d57a1b24d9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -571,8 +571,14 @@ interface IPackageManager {
/**
* Ask the package manager to dump profiles associated with a package.
+ *
+ * @param packageName The name of the package to dump.
+ * @param dumpClassesAndMethods If false, pass {@code --dump-only} to profman to dump the
+ * profile in a human readable form intended for debugging. If true, pass
+ * {@code --dump-classes-and-methods} to profman to dump a sorted list of classes and methods
+ * in a human readable form that is valid input for {@code profman --create-profile-from}.
*/
- void dumpProfiles(String packageName);
+ void dumpProfiles(String packageName, boolean dumpClassesAndMethods);
void forceDexOpt(String packageName);
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
index 584a058aaede..1e659b74db77 100644
--- a/core/java/android/content/pm/SigningDetails.java
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -112,6 +112,29 @@ public final class SigningDetails implements Parcelable {
int AUTH = 16;
}
+ @IntDef(value = {CapabilityMergeRule.MERGE_SELF_CAPABILITY,
+ CapabilityMergeRule.MERGE_OTHER_CAPABILITY,
+ CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY})
+ public @interface CapabilityMergeRule {
+ /**
+ * When capabilities are different for a common signer in the lineage, use the capabilities
+ * from this instance.
+ */
+ int MERGE_SELF_CAPABILITY = 0;
+
+ /**
+ * When capabilities are different for a common signer in the lineage, use the capabilites
+ * from the other instance.
+ */
+ int MERGE_OTHER_CAPABILITY = 1;
+
+ /**
+ * When capabilities are different for a common signer in the lineage, use the most
+ * restrictive between the two signers.
+ */
+ int MERGE_RESTRICTED_CAPABILITY = 2;
+ }
+
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
@@ -164,30 +187,60 @@ public final class SigningDetails implements Parcelable {
/**
* Merges the signing lineage of this instance with the lineage in the provided {@code
- * otherSigningDetails} when one has the same or an ancestor signer of the other.
+ * otherSigningDetails} using {@link CapabilityMergeRule#MERGE_OTHER_CAPABILITY} as the merge
+ * rule.
+ *
+ * @param otherSigningDetails the {@code SigningDetails} with which to merge
+ * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
+ * of the other. If neither instance has a lineage, or if neither has the same or an
+ * ancestor signer then this instance is returned.
+ * @see #mergeLineageWith(SigningDetails, int)
+ */
+ public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+ return mergeLineageWith(otherSigningDetails, CapabilityMergeRule.MERGE_OTHER_CAPABILITY);
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage in the provided {@code
+ * otherSigningDetails} when one has the same or an ancestor signer of the other using the
+ * provided {@code mergeRule} to handle differences in capabilities for shared signers.
*
* <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
- * containing the longest common lineage with the most restrictive capabilities. If the two
- * lineages contain the same signers with the same capabilities then the instance on which
- * this was invoked is returned without any changes. Similarly if neither instance has a
- * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+ * containing the longest common lineage with differences in capabilities for shared signers
+ * resolved using the provided {@code mergeRule}. If the two lineages contain the same signers
+ * with the same capabilities then the instance on which this was invoked is returned without
+ * any changes. Similarly if neither instance has a lineage, or if neither has the same or an
+ * ancestor signer then this instance is returned.
*
* Following are some example results of this method for lineages with signers A, B, C, D:
- * - lineage B merged with lineage A -> B returns lineage A -> B.
- * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
- * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
- * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
- * lineage A -> B with both capabilities revoked for A.
- * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
- * A -> B -> C since the current signer of both instances is not the same or in the
- * lineage of the other.
+ * <ul>
+ * <li>lineage B merged with lineage A -> B returns lineage A -> B.
+ * <li>lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+ * <li>lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+ * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns the
+ * following based on the {@code mergeRule}:
+ * <ul>
+ * <li>{@code MERGE_SELF_CAPABILITY} - lineage A -> B with {@code PERMISSION} revoked
+ * for A.
+ * <li>{@code MERGE_OTHER_CAPABILITY} - lineage A -> B with {@code SHARED_USER_ID}
+ * revoked for A.
+ * <li>{@code MERGE_RESTRICTED_CAPABILITY} - lineage A -> B with {@code PERMISSION} and
+ * {@code SHARED_USER_ID} revoked for A.
+ * </ul>
+ * <li>lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+ * A -> B -> C since the current signer of both instances is not the same or in the
+ * lineage of the other.
+ * </ul>
*
- * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @param otherSigningDetails the {@code SigningDetails} with which to merge
+ * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in
+ * capabilities for shared signers
* @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
* of the other. If neither instance has a lineage, or if neither has the same or an
* ancestor signer then this instance is returned.
*/
- public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+ public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails,
+ @CapabilityMergeRule int mergeRule) {
if (!hasPastSigningCertificates()) {
return otherSigningDetails.hasPastSigningCertificates()
&& otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
@@ -201,19 +254,43 @@ public final class SigningDetails implements Parcelable {
if (descendantSigningDetails == null) {
return this;
}
- return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
- otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+ SigningDetails mergedDetails = this;
+ if (descendantSigningDetails == this) {
+ // If this instance is the descendant then the merge will also be invoked against this
+ // instance and the provided mergeRule can be used as is.
+ mergedDetails = mergeLineageWithAncestorOrSelf(otherSigningDetails, mergeRule);
+ } else {
+ // If the provided instance is the descendant then the merge will be invoked against the
+ // other instance and a self or other merge rule will need to be flipped.
+ switch (mergeRule) {
+ case CapabilityMergeRule.MERGE_SELF_CAPABILITY:
+ mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
+ CapabilityMergeRule.MERGE_OTHER_CAPABILITY);
+ break;
+ case CapabilityMergeRule.MERGE_OTHER_CAPABILITY:
+ mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
+ CapabilityMergeRule.MERGE_SELF_CAPABILITY);
+ break;
+ case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY:
+ mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this,
+ mergeRule);
+ break;
+ }
+ }
+ return mergedDetails;
}
/**
* Merges the signing lineage of this instance with the lineage of the ancestor (or same)
* signer in the provided {@code otherSigningDetails}.
*
- * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @param otherSigningDetails the {@code SigningDetails} with which to merge
+ * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in
+ * capabilities for shared signers
* @return Merged {@code SigningDetails} instance.
*/
private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
- @NonNull SigningDetails otherSigningDetails) {
+ @NonNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule) {
// This method should only be called with instances that contain lineages.
int index = mPastSigningCertificates.length - 1;
int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
@@ -236,16 +313,26 @@ public final class SigningDetails implements Parcelable {
}
do {
- // Add the common signer to the merged lineage with the most restrictive
- // capabilities of the two lineages.
+ // Add the common signer to the merged lineage and resolve any differences in
+ // capabilites with the merge rule.
Signature signature = mPastSigningCertificates[index--];
Signature ancestorSignature =
otherSigningDetails.mPastSigningCertificates[otherIndex--];
Signature mergedSignature = new Signature(signature);
- int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
- if (signature.getFlags() != mergedCapabilities) {
+ if (signature.getFlags() != ancestorSignature.getFlags()) {
capabilitiesModified = true;
- mergedSignature.setFlags(mergedCapabilities);
+ switch (mergeRule) {
+ case CapabilityMergeRule.MERGE_SELF_CAPABILITY:
+ mergedSignature.setFlags(signature.getFlags());
+ break;
+ case CapabilityMergeRule.MERGE_OTHER_CAPABILITY:
+ mergedSignature.setFlags(ancestorSignature.getFlags());
+ break;
+ case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY:
+ mergedSignature.setFlags(
+ signature.getFlags() & ancestorSignature.getFlags());
+ break;
+ }
}
mergedSignatures.add(mergedSignature);
} while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
@@ -858,7 +945,7 @@ public final class SigningDetails implements Parcelable {
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -914,10 +1001,10 @@ public final class SigningDetails implements Parcelable {
}
@DataClass.Generated(
- time = 1616984092921L,
- codegenVersion = "1.0.22",
+ time = 1650058974710L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails,int)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails,int)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index dd3ddafd4a09..29f21f14088e 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -763,6 +763,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* (for example, when apps are displayed side by side in split-screen mode
* in landscape orientation).
*
+ * <p>In multiple-screen scenarios, the width measurement can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device
+ * (with the screens side by side), {@code screenWidthDp} represents the
+ * width of both screens, excluding the area occupied by screen decorations.
+ * When the app is restricted to a single screen in a multiple-screen
+ * environment, {@code screenWidthDp} is the width of the screen on which
+ * the app is running.
+ *
* <p>Differs from {@link android.view.WindowMetrics} by not including
* screen decorations in the width measurement and by expressing the
* measurement in dp rather than px. Use {@code screenWidthDp} to obtain the
@@ -792,6 +800,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* (for example, when apps are displayed one above another in split-screen
* mode in portrait orientation).
*
+ * <p>In multiple-screen scenarios, the height measurement can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device
+ * rotated 90 degrees (one screen above the other), {@code screenHeightDp}
+ * represents the height of both screens, excluding the area occupied by
+ * screen decorations. When the app is restricted to a single screen in a
+ * multiple-screen environment, {@code screenHeightDp} is the height of the
+ * screen on which the app is running.
+ *
* <p>Differs from {@link android.view.WindowMetrics} by not including
* screen decorations in the height measurement and by expressing the
* measurement in dp rather than px. Use {@code screenHeightDp} to obtain
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.java b/core/java/android/hardware/biometrics/SensorLocationInternal.java
index fb25a2fcd823..25c0f7c02253 100644
--- a/core/java/android/hardware/biometrics/SensorLocationInternal.java
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.java
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -109,4 +110,12 @@ public class SensorLocationInternal implements Parcelable {
+ ", y: " + sensorLocationY
+ ", r: " + sensorRadius + "]";
}
+
+ /** Returns coordinates of a bounding box around the sensor. */
+ public Rect getRect() {
+ return new Rect(sensorLocationX - sensorRadius,
+ sensorLocationY - sensorRadius,
+ sensorLocationX + sensorRadius,
+ sensorLocationY + sensorRadius);
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index aa98f1fdf9bc..cf611fb3adcf 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -824,24 +824,34 @@ public final class CameraExtensionCharacteristics {
if (!isExtensionSupported(mCameraId, extension, mChars)) {
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
- extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- CameraMetadataNative captureRequestMeta =
- extenders.second.getAvailableCaptureRequestKeys();
+
+ CameraMetadataNative captureRequestMeta = null;
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId);
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
+ extenders.second.onDeInit();
+ }
if (captureRequestMeta != null) {
int[] requestKeys = captureRequestMeta.get(
CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
if (requestKeys == null) {
- throw new AssertionError("android.request.availableRequestKeys must be non-null"
- + " in the characteristics");
+ throw new AssertionError(
+ "android.request.availableRequestKeys must be non-null"
+ + " in the characteristics");
}
- CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta);
+ CameraCharacteristics requestChars = new CameraCharacteristics(
+ captureRequestMeta);
Object crKey = CaptureRequest.Key.class;
- Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+ Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey;
ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
requestKeys, /*includeSynthetic*/ false));
@@ -854,7 +864,6 @@ public final class CameraExtensionCharacteristics {
if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
ret.add(CaptureRequest.JPEG_ORIENTATION);
}
- extenders.second.onDeInit();
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
@@ -894,12 +903,19 @@ public final class CameraExtensionCharacteristics {
throw new IllegalArgumentException("Unsupported extension");
}
- Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
- initializeExtension(extension);
- extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
- extenders.second.init(mCameraId, mChars.getNativeMetadata());
- CameraMetadataNative captureResultMeta =
- extenders.second.getAvailableCaptureResultKeys();
+ CameraMetadataNative captureResultMeta = null;
+ if (areAdvancedExtensionsSupported()) {
+ IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+ extender.init(mCameraId);
+ captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId);
+ } else {
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
+ extenders.second.onDeInit();
+ }
if (captureResultMeta != null) {
int[] resultKeys = captureResultMeta.get(
@@ -926,7 +942,6 @@ public final class CameraExtensionCharacteristics {
ret.add(CaptureResult.SENSOR_TIMESTAMP);
}
}
- extenders.second.onDeInit();
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index ee3441fc16f1..6ddaddf8a809 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -265,8 +265,8 @@ public abstract class CameraExtensionSession implements AutoCloseable {
* from the camera device, to produce a single high-quality output result.
*
* <p>Note that single capture requests currently do not support
- * client parameters except for {@link CaptureRequest#JPEG_ORIENTATION orientation} and
- * {@link CaptureRequest#JPEG_QUALITY quality} in case of ImageFormat.JPEG output target.
+ * client parameters except for controls advertised in
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
* The rest of the settings included in the request will be entirely overridden by
* the device-specific extension. </p>
*
@@ -275,6 +275,11 @@ public abstract class CameraExtensionSession implements AutoCloseable {
* arguments that include further targets will cause
* IllegalArgumentException to be thrown. </p>
*
+ * <p>Starting with Android {@link android.os.Build.VERSION_CODES#TIRAMISU} single capture
+ * requests will also support the preview {@link android.graphics.ImageFormat#PRIVATE} target
+ * surface. These can typically be used for enabling AF/AE triggers. Do note, that single
+ * capture requests referencing both output surfaces remain unsupported.</p>
+ *
* <p>Each request will produce one new frame for one target Surface, set
* with the CaptureRequest builder's
* {@link CaptureRequest.Builder#addTarget} method.</p>
@@ -319,8 +324,10 @@ public abstract class CameraExtensionSession implements AutoCloseable {
* rate possible.</p>
*
* <p>Note that repeating capture requests currently do not support
- * client parameters. Settings included in the request will
- * be completely overridden by the device-specific extension.</p>
+ * client parameters except for controls advertised in
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
+ * The rest of the settings included in the request will be entirely overridden by
+ * the device-specific extension. </p>
*
* <p>The {@link CaptureRequest.Builder#addTarget} supports only one
* target surface. {@link CaptureRequest} arguments that include further
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
index f279c59b812c..935a542b72aa 100644
--- a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -19,6 +19,7 @@ import android.hardware.camera2.extension.ISessionProcessorImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.Size;
import android.hardware.camera2.extension.SizeList;
+import android.hardware.camera2.impl.CameraMetadataNative;
/** @hide */
interface IAdvancedExtenderImpl
@@ -30,4 +31,6 @@ interface IAdvancedExtenderImpl
@nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
@nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
ISessionProcessorImpl getSessionProcessor();
+ CameraMetadataNative getAvailableCaptureRequestKeys(in String cameraId);
+ CameraMetadataNative getAvailableCaptureResultKeys(in String cameraId);
}
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
index 6ab0ad2c7417..f3062ad980a9 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -16,6 +16,7 @@
package android.hardware.camera2.extension;
import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.impl.CameraMetadataNative;
/** @hide */
interface ICaptureCallback
@@ -25,4 +26,5 @@ interface ICaptureCallback
void onCaptureFailed(int captureSequenceId);
void onCaptureSequenceCompleted(int captureSequenceId);
void onCaptureSequenceAborted(int captureSequenceId);
+ void onCaptureCompleted(long shutterTimestamp, int requestId, in CameraMetadataNative results);
}
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
index 6fdf4df775bc..0eca5a7f4c40 100644
--- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.extension;
+import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.extension.CameraSessionConfig;
import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl;
@@ -30,5 +31,7 @@ interface ISessionProcessorImpl
void onCaptureSessionEnd();
int startRepeating(in ICaptureCallback callback);
void stopRepeating();
- int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
+ int startCapture(in ICaptureCallback callback);
+ void setParameters(in CaptureRequest captureRequest);
+ int startTrigger(in CaptureRequest captureRequest, in ICaptureCallback callback);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 3c52d65b2f00..5503e2834d98 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -86,6 +86,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
// maps camera extension output ids to camera registered image readers
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
private final RequestProcessor mRequestProcessor = new RequestProcessor();
+ private final int mSessionId;
private Surface mClientRepeatingRequestSurface;
private Surface mClientCaptureSurface;
@@ -175,7 +176,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
- config.getStateCallback(), config.getExecutor());
+ config.getStateCallback(), config.getExecutor(), sessionId);
ret.initialize();
return ret;
@@ -184,7 +185,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
private CameraAdvancedExtensionSessionImpl(long extensionClientId,
@NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
@Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
- @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
+ @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor,
+ int sessionId) {
mExtensionClientId = extensionClientId;
mAdvancedExtender = extender;
mCameraDevice = cameraDevice;
@@ -197,6 +199,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
+ mSessionId = sessionId;
}
/**
@@ -367,6 +370,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
}
try {
+ mSessionProcessor.setParameters(request);
+
seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
executor, listener));
} catch (RemoteException e) {
@@ -388,34 +393,32 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
throw new IllegalStateException("Uninitialized component");
}
- if (mClientCaptureSurface == null) {
- throw new IllegalArgumentException("No output surface registered for single"
- + " requests!");
+ if (request.getTargets().size() != 1) {
+ throw new IllegalArgumentException("Single capture to both preview & still" +
+ " capture outputs target is not supported!");
}
- if (!request.containsTarget(mClientCaptureSurface) ||
- (request.getTargets().size() != 1)) {
- throw new IllegalArgumentException("Invalid single capture output target!");
- }
+ if ((mClientCaptureSurface != null) && request.containsTarget(mClientCaptureSurface)) {
+ try {
+ mSessionProcessor.setParameters(request);
- try {
- // This will override the extension capture stage jpeg parameters with the user set
- // jpeg quality and rotation. This will guarantee that client configured jpeg
- // parameters always have highest priority.
- Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
- if (jpegRotation == null) {
- jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+ seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
+ executor, listener));
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
+ " to submit capture request, extension service failed to respond!");
}
- Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
- if (jpegQuality == null) {
- jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+ } else if ((mClientRepeatingRequestSurface != null) &&
+ request.containsTarget(mClientRepeatingRequestSurface)) {
+ try {
+ seqId = mSessionProcessor.startTrigger(request,
+ new RequestCallbackHandler(request, executor, listener));
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
+ " to submit trigger request, extension service failed to respond!");
}
-
- seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
- executor, listener), jpegRotation, jpegQuality);
- } catch (RemoteException e) {
- throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
- "Failed to submit capture request, extension service failed to respond!");
+ } else {
+ throw new IllegalArgumentException("Invalid single capture output target!");
}
}
@@ -661,6 +664,28 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void onCaptureCompleted(long timestamp, int requestId, CameraMetadataNative result) {
+ if (result == null) {
+ Log.e(TAG,"Invalid capture result!");
+ return;
+ }
+
+ result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+ TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
+ mClientRequest, requestId, timestamp, new ArrayList<>(), mSessionId,
+ new PhysicalCaptureResultInfo[0]);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mClientCallbacks.onCaptureResultAvailable(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+ totalResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 1514a2be5de8..aee20db6783e 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -303,6 +303,7 @@ public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
jpegBuffer, jpegCapacity, jpegParams.mQuality,
0, 0, yuvImage.getWidth(), yuvImage.getHeight(),
jpegParams.mRotation);
+ jpegImage.setTimestamp(yuvImage.getTimestamp());
yuvImage.close();
try {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 916d16d92977..1263da6e4884 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -475,8 +475,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mInternalRepeatingRequestEnabled = false;
try {
return setRepeatingRequest(mPreviewExtender.getCaptureStage(),
- new RepeatingRequestHandler(request, executor, listener,
- mRepeatingRequestImageCallback));
+ new PreviewRequestHandler(request, executor, listener,
+ mRepeatingRequestImageCallback), request);
} catch (RemoteException e) {
Log.e(TAG, "Failed to set repeating request! Extension service does not "
+ "respond");
@@ -530,7 +530,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
CaptureRequest request = requestBuilder.build();
CameraMetadataNative.update(request.getNativeMetadata(), captureStage.parameters);
ret.add(request);
- captureMap.put(request, captureStage.id);
+ if (captureMap != null) {
+ captureMap.put(request, captureStage.id);
+ }
}
return ret;
@@ -583,33 +585,57 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
throw new IllegalStateException("Uninitialized component");
}
- if (mClientCaptureSurface == null) {
- throw new IllegalArgumentException("No output surface registered for single requests!");
+ if (request.getTargets().size() != 1) {
+ throw new IllegalArgumentException("Single capture to both preview & still capture " +
+ "outputs target is not supported!");
}
- if (!request.containsTarget(mClientCaptureSurface) || (request.getTargets().size() != 1)) {
- throw new IllegalArgumentException("Invalid single capture output target!");
- }
+ int seqId = -1;
+ if ((mClientCaptureSurface != null) && request.containsTarget(mClientCaptureSurface)) {
+ HashMap<CaptureRequest, Integer> requestMap = new HashMap<>();
+ List<CaptureRequest> burstRequest;
+ try {
+ burstRequest = createBurstRequest(mCameraDevice,
+ mImageExtender.getCaptureStages(), request, mCameraBurstSurface,
+ CameraDevice.TEMPLATE_STILL_CAPTURE, requestMap);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to initialize internal burst request! Extension service does"
+ + " not respond!");
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
+ }
+ if (burstRequest == null) {
+ throw new UnsupportedOperationException(
+ "Failed to create still capture burst request");
+ }
- HashMap<CaptureRequest, Integer> requestMap = new HashMap<>();
- List<CaptureRequest> burstRequest;
- try {
- burstRequest = createBurstRequest(mCameraDevice,
- mImageExtender.getCaptureStages(), request, mCameraBurstSurface,
- CameraDevice.TEMPLATE_STILL_CAPTURE, requestMap);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to initialize internal burst request! Extension service does"
- + " not respond!");
- throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
- }
- if (burstRequest == null) {
- throw new UnsupportedOperationException("Failed to create still capture burst request");
+ seqId = mCaptureSession.captureBurstRequests(burstRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler),
+ new BurstRequestHandler(request, executor, listener, requestMap,
+ mBurstCaptureImageCallback));
+ } else if ((mClientRepeatingRequestSurface != null) &&
+ request.containsTarget(mClientRepeatingRequestSurface)) {
+
+ CaptureRequest captureRequest = null;
+ try {
+ ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
+ captureStageList.add(mPreviewExtender.getCaptureStage());
+
+ captureRequest = createRequest(mCameraDevice, captureStageList,
+ mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, request);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to initialize capture request! Extension service does"
+ + " not respond!");
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
+ }
+
+ seqId = mCaptureSession.capture(captureRequest, new PreviewRequestHandler(request,
+ executor, listener, mRepeatingRequestImageCallback, true /*singleCapture*/),
+ mHandler);
+ } else {
+ throw new IllegalArgumentException("Capture request to unknown output surface!");
}
- return mCaptureSession.captureBurstRequests(burstRequest,
- new CameraExtensionUtils.HandlerExecutor(mHandler),
- new BurstRequestHandler(request, executor, listener, requestMap,
- mBurstCaptureImageCallback));
+ return seqId;
}
@Override
@@ -847,7 +873,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
} else {
try {
setRepeatingRequest(mPreviewExtender.getCaptureStage(),
- new RepeatingRequestHandler(null, null, null,
+ new PreviewRequestHandler(null, null, null,
mRepeatingRequestImageCallback));
} catch (CameraAccessException | RemoteException e) {
Log.e(TAG,
@@ -1010,7 +1036,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
if (timestamp != null) {
if (mCaptureResultsSupported && (mCaptureResultHandler == null)) {
mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
- mCallbacks, result.getSessionId());
+ mCallbacks, result.getSequenceId());
}
if (mImageProcessor != null) {
if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
@@ -1179,7 +1205,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
*/
try {
setRepeatingRequest(mPreviewExtender.getCaptureStage(),
- new RepeatingRequestHandler(null, null, null,
+ new PreviewRequestHandler(null, null, null,
mImageCallback));
} catch (CameraAccessException | RemoteException e) {
Log.e(TAG, "Failed to start the internal repeating request!");
@@ -1320,17 +1346,20 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
}
- // This handler can operate in two modes:
+ // This handler can operate in three modes:
// 1) Using valid client callbacks, which means camera buffers will be propagated the
// registered output surfaces and clients will be notified accordingly.
// 2) Without any client callbacks where an internal repeating request is kept active
// to satisfy the extensions continuous preview/(repeating request) requirement.
- private class RepeatingRequestHandler extends CameraCaptureSession.CaptureCallback {
+ // 3) Single capture mode, where internal repeating requests are ignored and the preview
+ // logic is only triggered for the image processor case.
+ private class PreviewRequestHandler extends CameraCaptureSession.CaptureCallback {
private final Executor mExecutor;
private final ExtensionCaptureCallback mCallbacks;
private final CaptureRequest mClientRequest;
private final boolean mClientNotificationsEnabled;
private final CameraOutputImageCallback mRepeatingImageCallback;
+ private final boolean mSingleCapture;
private OnImageAvailableListener mImageCallback = null;
private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap =
new LongSparseArray<>();
@@ -1338,15 +1367,22 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private boolean mRequestUpdatedNeeded = false;
- public RepeatingRequestHandler(@Nullable CaptureRequest clientRequest,
+ public PreviewRequestHandler(@Nullable CaptureRequest clientRequest,
@Nullable Executor executor, @Nullable ExtensionCaptureCallback listener,
@NonNull CameraOutputImageCallback imageCallback) {
+ this(clientRequest, executor, listener, imageCallback, false /*singleCapture*/);
+ }
+
+ public PreviewRequestHandler(@Nullable CaptureRequest clientRequest,
+ @Nullable Executor executor, @Nullable ExtensionCaptureCallback listener,
+ @NonNull CameraOutputImageCallback imageCallback, boolean singleCapture) {
mClientRequest = clientRequest;
mExecutor = executor;
mCallbacks = listener;
mClientNotificationsEnabled =
(mClientRequest != null) && (mExecutor != null) && (mCallbacks != null);
mRepeatingImageCallback = imageCallback;
+ mSingleCapture = singleCapture;
}
@Override
@@ -1393,7 +1429,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
int sequenceId) {
synchronized (mInterfaceLock) {
- if (mInternalRepeatingRequestEnabled) {
+ if (mInternalRepeatingRequestEnabled && !mSingleCapture) {
resumeInternalRepeatingRequest(true);
}
}
@@ -1420,10 +1456,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
long frameNumber) {
synchronized (mInterfaceLock) {
- if (mRequestUpdatedNeeded) {
+ if (mRequestUpdatedNeeded && !mSingleCapture) {
mRequestUpdatedNeeded = false;
resumeInternalRepeatingRequest(false);
- } else if (mInternalRepeatingRequestEnabled) {
+ } else if (mInternalRepeatingRequestEnabled && !mSingleCapture) {
resumeInternalRepeatingRequest(true);
}
}
@@ -1471,10 +1507,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
if (mCaptureResultsSupported && mClientNotificationsEnabled &&
(mCaptureResultHandler == null)) {
mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
- mCallbacks, result.getSessionId());
+ mCallbacks, result.getSequenceId());
}
- if (mPreviewProcessorType ==
- IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
+ if ((!mSingleCapture) && (mPreviewProcessorType ==
+ IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY)) {
CaptureStageImpl captureStage = null;
try {
captureStage = mPreviewRequestUpdateProcessor.process(
@@ -1582,10 +1618,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
try {
if (internal) {
setRepeatingRequest(mPreviewExtender.getCaptureStage(),
- new RepeatingRequestHandler(null, null, null,
+ new PreviewRequestHandler(null, null, null,
mRepeatingImageCallback));
} else {
- setRepeatingRequest(mPreviewExtender.getCaptureStage(), this);
+ setRepeatingRequest(mPreviewExtender.getCaptureStage(), this, mClientRequest);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to resume internal repeating request, extension service"
@@ -1733,7 +1769,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
private static Size findSmallestAspectMatchedSize(@NonNull List<Size> sizes,
- @NonNull Size arSize) {
+ @NonNull Size arSize) {
final float TOLL = .01f;
if (arSize.getHeight() == 0) {
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 75356d1ce994..eccbb403b306 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -41,9 +41,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
-/** @hide */
-// TODO(b/215636776): move IInputMethodSessionWrapper to proper package
-public class IInputMethodSessionWrapper extends IInputMethodSession.Stub
+class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
index 09dbb27359b0..f44f49d7dcaf 100644
--- a/core/java/android/inputmethodservice/InputMethodServiceInternal.java
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -32,11 +32,8 @@ import java.io.PrintWriter;
* framework classes for internal use.
*
* <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
- *
- * @hide
*/
-// TODO(b/215636776): move InputMethodServiceInternal to proper package
-public interface InputMethodServiceInternal {
+interface InputMethodServiceInternal {
/**
* @return {@link Context} associated with the service.
*/
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 5b0129ee814c..86e59e9dcf2f 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -53,11 +53,8 @@ import java.util.concurrent.CompletableFuture;
*
* <p>See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under
* the hood.</p>
- *
- * @hide
*/
-// TODO(b/215636776): move RemoteInputConnection to proper package
-public final class RemoteInputConnection implements InputConnection {
+final class RemoteInputConnection implements InputConnection {
private static final String TAG = "RemoteInputConnection";
private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -98,7 +95,7 @@ public final class RemoteInputConnection implements InputConnection {
@NonNull
private final CancellationGroup mCancellationGroup;
- public RemoteInputConnection(
+ RemoteInputConnection(
@NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) {
mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
@@ -111,7 +108,7 @@ public final class RemoteInputConnection implements InputConnection {
return mInvoker.isSameConnection(inputContext);
}
- public RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
+ RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
mImsInternal = original.mImsInternal;
mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
mCancellationGroup = original.mCancellationGroup;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 714227d87aeb..570211ecc88d 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -396,7 +396,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
return true;
case MATCH_CARRIER:
case MATCH_MOBILE:
- return !template.getSubscriberIds().isEmpty();
+ return !template.getSubscriberIds().isEmpty()
+ && template.getMeteredness() == METERED_YES;
case MATCH_WIFI:
if (template.getWifiNetworkKeys().isEmpty()
&& template.getSubscriberIds().isEmpty()) {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index f1b110ab29c8..40e4083c02db 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -104,6 +104,14 @@ public class VcnManager {
// TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
+ /** List of Carrier Config options to extract from Carrier Config bundles. @hide */
+ @NonNull
+ public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS =
+ new String[] {
+ VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY
+ };
+
private static final Map<
VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 296877a448ab..78f91edfedcd 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -26,6 +26,7 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
@@ -306,6 +307,13 @@ public final class FillResponse implements Parcelable {
* with the fully populated {@link FillResponse response} (or {@code null} if the screen
* cannot be autofilled).
*
+ * <p> <b>IMPORTANT</b>: Extras must be non-null on the intent being set for Android 12
+ * otherwise it will cause a crash. Do not use {@link Activity#setResult(int)}, instead use
+ * {@link Activity#setResult(int, Intent) with non-null extras. Consider setting {
+ * @link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} to null or use
+ * {@link Bundle#EMPTY} with {@link Intent#putExtras(Bundle)} on the intent when
+ * finishing activity to avoid crash). </p>
+ *
* <p>For example, if you provided an empty {@link FillResponse response} because the
* user's data was locked and marked that the response needs an authentication then
* in the response returned if authentication succeeds you need to provide all
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index bfc3b8b39385..163d6ed4b18b 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -36,9 +36,6 @@ public abstract class DreamOverlayService extends Service {
private static final String TAG = "DreamOverlayService";
private static final boolean DEBUG = false;
private boolean mShowComplications;
- private boolean mIsPreviewMode;
- @Nullable
- private CharSequence mDreamLabel;
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
@Override
@@ -59,8 +56,6 @@ public abstract class DreamOverlayService extends Service {
public final IBinder onBind(@NonNull Intent intent) {
mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
DreamService.DEFAULT_SHOW_COMPLICATIONS);
- mIsPreviewMode = intent.getBooleanExtra(DreamService.EXTRA_IS_PREVIEW, false);
- mDreamLabel = intent.getCharSequenceExtra(DreamService.EXTRA_DREAM_LABEL);
return mDreamOverlay.asBinder();
}
@@ -89,19 +84,4 @@ public abstract class DreamOverlayService extends Service {
public final boolean shouldShowComplications() {
return mShowComplications;
}
-
- /**
- * Returns whether the dream is running in preview mode.
- */
- public final boolean isPreviewMode() {
- return mIsPreviewMode;
- }
-
- /**
- * Returns the user-facing label of the currently running dream.
- */
- @Nullable
- public final CharSequence getDreamLabel() {
- return mDreamLabel;
- }
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 95eae6cada9f..6a9afdb84e18 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -216,18 +216,6 @@ public class DreamService extends Service implements Window.Callback {
"android.service.dreams.SHOW_COMPLICATIONS";
/**
- * Extra containing a boolean for whether we are showing this dream in preview mode.
- * @hide
- */
- public static final String EXTRA_IS_PREVIEW = "android.service.dreams.IS_PREVIEW";
-
- /**
- * The user-facing label of the current dream service.
- * @hide
- */
- public static final String EXTRA_DREAM_LABEL = "android.service.dreams.DREAM_LABEL";
-
- /**
* The default value for whether to show complications on the overlay.
* @hide
*/
@@ -270,7 +258,7 @@ public class DreamService extends Service implements Window.Callback {
}
public void bind(Context context, @Nullable ComponentName overlayService,
- ComponentName dreamService, boolean isPreviewMode) {
+ ComponentName dreamService) {
if (overlayService == null) {
return;
}
@@ -281,8 +269,6 @@ public class DreamService extends Service implements Window.Callback {
overlayIntent.setComponent(overlayService);
overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
fetchShouldShowComplications(context, serviceInfo));
- overlayIntent.putExtra(EXTRA_DREAM_LABEL, fetchDreamLabel(context, serviceInfo));
- overlayIntent.putExtra(EXTRA_IS_PREVIEW, isPreviewMode);
context.bindService(overlayIntent,
this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
@@ -1007,8 +993,7 @@ public class DreamService extends Service implements Window.Callback {
mOverlayConnection.bind(
/* context= */ this,
intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
- new ComponentName(this, getClass()),
- intent.getBooleanExtra(EXTRA_IS_PREVIEW, /* defaultValue= */ false));
+ new ComponentName(this, getClass()));
}
return mDreamServiceWrapper;
diff --git a/core/java/android/util/DisplayUtils.java b/core/java/android/util/DisplayUtils.java
index 4fe7f8369f73..9b76fc2ff028 100644
--- a/core/java/android/util/DisplayUtils.java
+++ b/core/java/android/util/DisplayUtils.java
@@ -21,7 +21,7 @@ import android.content.res.Resources;
import com.android.internal.R;
/**
- * Utils for loading resources for multi-display.
+ * Utils for loading display related resources and calculations.
*
* @hide
*/
@@ -51,4 +51,17 @@ public class DisplayUtils {
}
return index;
}
+
+ /**
+ * Get the display size ratio based on the stable display size.
+ */
+ public static float getPhysicalPixelDisplaySizeRatio(
+ int stableWidth, int stableHeight, int currentWidth, int currentHeight) {
+ if (stableWidth == currentWidth && stableHeight == currentHeight) {
+ return 1f;
+ }
+ final float widthRatio = (float) currentWidth / stableWidth;
+ final float heightRatio = (float) currentHeight / stableHeight;
+ return Math.min(widthRatio, heightRatio);
+ }
}
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index cebdbf669f3a..c54d9b604ba2 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -88,14 +88,14 @@ public class RotationUtils {
}
/**
- * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
- * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
- * remains at 0,0 after rotation. The bounds will be at the same physical position in
- * parentBounds.
+ * Rotates inOutBounds together with the parent for a given rotation delta. This assumes that
+ * the parent starts at 0,0 and remains at 0,0 after the rotation. The inOutBounds will remain
+ * at the same physical position within the parent.
*
* Only 'inOutBounds' is mutated.
*/
- public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
+ public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
+ @Rotation int rotation) {
final int origLeft = inOutBounds.left;
final int origTop = inOutBounds.top;
switch (rotation) {
@@ -103,24 +103,36 @@ public class RotationUtils {
return;
case ROTATION_90:
inOutBounds.left = inOutBounds.top;
- inOutBounds.top = parentBounds.right - inOutBounds.right;
+ inOutBounds.top = parentWidth - inOutBounds.right;
inOutBounds.right = inOutBounds.bottom;
- inOutBounds.bottom = parentBounds.right - origLeft;
+ inOutBounds.bottom = parentWidth - origLeft;
return;
case ROTATION_180:
- inOutBounds.left = parentBounds.right - inOutBounds.right;
- inOutBounds.right = parentBounds.right - origLeft;
- inOutBounds.top = parentBounds.bottom - inOutBounds.bottom;
- inOutBounds.bottom = parentBounds.bottom - origTop;
+ inOutBounds.left = parentWidth - inOutBounds.right;
+ inOutBounds.right = parentWidth - origLeft;
+ inOutBounds.top = parentHeight - inOutBounds.bottom;
+ inOutBounds.bottom = parentHeight - origTop;
return;
case ROTATION_270:
- inOutBounds.left = parentBounds.bottom - inOutBounds.bottom;
+ inOutBounds.left = parentHeight - inOutBounds.bottom;
inOutBounds.bottom = inOutBounds.right;
- inOutBounds.right = parentBounds.bottom - inOutBounds.top;
+ inOutBounds.right = parentHeight - inOutBounds.top;
inOutBounds.top = origLeft;
}
}
+ /**
+ * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
+ * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
+ * remains at 0,0 after rotation. The bounds will be at the same physical position in
+ * parentBounds.
+ *
+ * Only 'inOutBounds' is mutated.
+ */
+ public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
+ rotateBounds(inOutBounds, parentBounds.right, parentBounds.bottom, rotation);
+ }
+
/** @return the rotation needed to rotate from oldRotation to newRotation. */
@Rotation
public static int deltaRotation(@Rotation int oldRotation, @Rotation int newRotation) {
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index 850e9fc0db7e..bb6005c76313 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -94,7 +94,7 @@ public class CutoutSpecification {
private final Rect mTopBound;
private final Rect mRightBound;
private final Rect mBottomBound;
- private final Insets mInsets;
+ private Insets mInsets;
private CutoutSpecification(@NonNull Parser parser) {
mPath = parser.mPath;
@@ -104,6 +104,8 @@ public class CutoutSpecification {
mBottomBound = parser.mBottomBound;
mInsets = parser.mInsets;
+ applyPhysicalPixelDisplaySizeRatio(parser.mPhysicalPixelDisplaySizeRatio);
+
if (DEBUG) {
Log.d(TAG, String.format(Locale.ENGLISH,
"left cutout = %s, top cutout = %s, right cutout = %s, bottom cutout = %s",
@@ -114,6 +116,38 @@ public class CutoutSpecification {
}
}
+ private void applyPhysicalPixelDisplaySizeRatio(float physicalPixelDisplaySizeRatio) {
+ if (physicalPixelDisplaySizeRatio == 1f) {
+ return;
+ }
+
+ if (mPath != null && !mPath.isEmpty()) {
+ final Matrix matrix = new Matrix();
+ matrix.postScale(physicalPixelDisplaySizeRatio, physicalPixelDisplaySizeRatio);
+ mPath.transform(matrix);
+ }
+
+ scaleBounds(mLeftBound, physicalPixelDisplaySizeRatio);
+ scaleBounds(mTopBound, physicalPixelDisplaySizeRatio);
+ scaleBounds(mRightBound, physicalPixelDisplaySizeRatio);
+ scaleBounds(mBottomBound, physicalPixelDisplaySizeRatio);
+ mInsets = scaleInsets(mInsets, physicalPixelDisplaySizeRatio);
+ }
+
+ private void scaleBounds(Rect r, float ratio) {
+ if (r != null && !r.isEmpty()) {
+ r.scale(ratio);
+ }
+ }
+
+ private Insets scaleInsets(Insets insets, float ratio) {
+ return Insets.of(
+ (int) (insets.left * ratio + 0.5f),
+ (int) (insets.top * ratio + 0.5f),
+ (int) (insets.right * ratio + 0.5f),
+ (int) (insets.bottom * ratio + 0.5f));
+ }
+
@VisibleForTesting(visibility = PACKAGE)
@Nullable
public Path getPath() {
@@ -168,9 +202,10 @@ public class CutoutSpecification {
@VisibleForTesting(visibility = PACKAGE)
public static class Parser {
private final boolean mIsShortEdgeOnTop;
- private final float mDensity;
- private final int mDisplayWidth;
- private final int mDisplayHeight;
+ private final float mStableDensity;
+ private final int mStableDisplayWidth;
+ private final int mStableDisplayHeight;
+ private final float mPhysicalPixelDisplaySizeRatio;
private final Matrix mMatrix;
private Insets mInsets;
private int mSafeInsetLeft;
@@ -202,19 +237,26 @@ public class CutoutSpecification {
private boolean mIsTouchShortEdgeEnd;
private boolean mIsCloserToStartSide;
+ @VisibleForTesting(visibility = PACKAGE)
+ public Parser(float stableDensity, int stableDisplayWidth, int stableDisplayHeight) {
+ this(stableDensity, stableDisplayWidth, stableDisplayHeight, 1f);
+ }
+
/**
* The constructor of the CutoutSpecification parser to parse the specification of cutout.
- * @param density the display density.
- * @param displayWidth the display width.
- * @param displayHeight the display height.
+ * @param stableDensity the display density.
+ * @param stableDisplayWidth the display width.
+ * @param stableDisplayHeight the display height.
+ * @param physicalPixelDisplaySizeRatio the display size ratio based on stable display size.
*/
- @VisibleForTesting(visibility = PACKAGE)
- public Parser(float density, int displayWidth, int displayHeight) {
- mDensity = density;
- mDisplayWidth = displayWidth;
- mDisplayHeight = displayHeight;
+ Parser(float stableDensity, int stableDisplayWidth, int stableDisplayHeight,
+ float physicalPixelDisplaySizeRatio) {
+ mStableDensity = stableDensity;
+ mStableDisplayWidth = stableDisplayWidth;
+ mStableDisplayHeight = stableDisplayHeight;
+ mPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
mMatrix = new Matrix();
- mIsShortEdgeOnTop = mDisplayWidth < mDisplayHeight;
+ mIsShortEdgeOnTop = mStableDisplayWidth < mStableDisplayHeight;
}
private void computeBoundsRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
@@ -239,38 +281,38 @@ public class CutoutSpecification {
private void translateMatrix() {
final float offsetX;
if (mPositionFromRight) {
- offsetX = mDisplayWidth;
+ offsetX = mStableDisplayWidth;
} else if (mPositionFromLeft) {
offsetX = 0;
} else {
- offsetX = mDisplayWidth / 2f;
+ offsetX = mStableDisplayWidth / 2f;
}
final float offsetY;
if (mPositionFromBottom) {
- offsetY = mDisplayHeight;
+ offsetY = mStableDisplayHeight;
} else if (mPositionFromCenterVertical) {
- offsetY = mDisplayHeight / 2f;
+ offsetY = mStableDisplayHeight / 2f;
} else {
offsetY = 0;
}
mMatrix.reset();
if (mInDp) {
- mMatrix.postScale(mDensity, mDensity);
+ mMatrix.postScale(mStableDensity, mStableDensity);
}
mMatrix.postTranslate(offsetX, offsetY);
}
private int computeSafeInsets(int gravity, Rect rect) {
- if (gravity == LEFT && rect.right > 0 && rect.right < mDisplayWidth) {
+ if (gravity == LEFT && rect.right > 0 && rect.right < mStableDisplayWidth) {
return rect.right;
- } else if (gravity == TOP && rect.bottom > 0 && rect.bottom < mDisplayHeight) {
+ } else if (gravity == TOP && rect.bottom > 0 && rect.bottom < mStableDisplayHeight) {
return rect.bottom;
- } else if (gravity == RIGHT && rect.left > 0 && rect.left < mDisplayWidth) {
- return mDisplayWidth - rect.left;
- } else if (gravity == BOTTOM && rect.top > 0 && rect.top < mDisplayHeight) {
- return mDisplayHeight - rect.top;
+ } else if (gravity == RIGHT && rect.left > 0 && rect.left < mStableDisplayWidth) {
+ return mStableDisplayWidth - rect.left;
+ } else if (gravity == BOTTOM && rect.top > 0 && rect.top < mStableDisplayHeight) {
+ return mStableDisplayHeight - rect.top;
}
return 0;
}
@@ -373,12 +415,12 @@ public class CutoutSpecification {
if (mIsShortEdgeOnTop) {
mIsTouchShortEdgeStart = mTmpRect.top <= 0;
- mIsTouchShortEdgeEnd = mTmpRect.bottom >= mDisplayHeight;
- mIsCloserToStartSide = mTmpRect.centerY() < mDisplayHeight / 2;
+ mIsTouchShortEdgeEnd = mTmpRect.bottom >= mStableDisplayHeight;
+ mIsCloserToStartSide = mTmpRect.centerY() < mStableDisplayHeight / 2;
} else {
mIsTouchShortEdgeStart = mTmpRect.left <= 0;
- mIsTouchShortEdgeEnd = mTmpRect.right >= mDisplayWidth;
- mIsCloserToStartSide = mTmpRect.centerX() < mDisplayWidth / 2;
+ mIsTouchShortEdgeEnd = mTmpRect.right >= mStableDisplayWidth;
+ mIsCloserToStartSide = mTmpRect.centerX() < mStableDisplayWidth / 2;
}
setEdgeCutout(newPath);
@@ -476,7 +518,6 @@ public class CutoutSpecification {
}
parseSpecWithoutDp(spec);
-
mInsets = Insets.of(mSafeInsetLeft, mSafeInsetTop, mSafeInsetRight, mSafeInsetBottom);
return new CutoutSpecification(this);
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index b3b7f10d8c7e..ece8460014f5 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -77,8 +77,10 @@ public final class DisplayCutout {
private static final Rect ZERO_RECT = new Rect();
private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo(
- 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
- 0 /* rotation */, 0f /* scale */);
+ 0 /* displayWidth */, 0 /* stableDisplayHeight */,
+ 0 /* stableDisplayHeight */, 0 /* displayHeight */, 0f /* density */,
+ "" /* cutoutSpec */, 0 /* ROTATION_0 */, 0f /* scale */,
+ 0f /* physicalPixelDisplaySizeRatio*/);
/**
* An instance where {@link #isEmpty()} returns {@code true}.
@@ -105,6 +107,8 @@ public final class DisplayCutout {
private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
@GuardedBy("CACHE_LOCK")
private static Insets sCachedWaterfallInsets;
+ @GuardedBy("CACHE_LOCK")
+ private static float sCachedPhysicalPixelDisplaySizeRatio;
@GuardedBy("CACHE_LOCK")
private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
@@ -254,28 +258,38 @@ public final class DisplayCutout {
public static class CutoutPathParserInfo {
private final int mDisplayWidth;
private final int mDisplayHeight;
+ private final int mStableDisplayWidth;
+ private final int mStableDisplayHeight;
private final float mDensity;
private final String mCutoutSpec;
private final @Rotation int mRotation;
private final float mScale;
+ private final float mPhysicalPixelDisplaySizeRatio;
- public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
- @Nullable String cutoutSpec, @Rotation int rotation, float scale) {
+ public CutoutPathParserInfo(int displayWidth, int displayHeight, int stableDisplayWidth,
+ int stableDisplayHeight, float density, @Nullable String cutoutSpec,
+ @Rotation int rotation, float scale, float physicalPixelDisplaySizeRatio) {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
+ mStableDisplayWidth = stableDisplayWidth;
+ mStableDisplayHeight = stableDisplayHeight;
mDensity = density;
mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec;
mRotation = rotation;
mScale = scale;
+ mPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
public CutoutPathParserInfo(@NonNull CutoutPathParserInfo cutoutPathParserInfo) {
mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
+ mStableDisplayWidth = cutoutPathParserInfo.mStableDisplayWidth;
+ mStableDisplayHeight = cutoutPathParserInfo.mStableDisplayHeight;
mDensity = cutoutPathParserInfo.mDensity;
mCutoutSpec = cutoutPathParserInfo.mCutoutSpec;
mRotation = cutoutPathParserInfo.mRotation;
mScale = cutoutPathParserInfo.mScale;
+ mPhysicalPixelDisplaySizeRatio = cutoutPathParserInfo.mPhysicalPixelDisplaySizeRatio;
}
public int getDisplayWidth() {
@@ -286,6 +300,14 @@ public final class DisplayCutout {
return mDisplayHeight;
}
+ public int getStableDisplayWidth() {
+ return mStableDisplayWidth;
+ }
+
+ public int getStableDisplayHeight() {
+ return mStableDisplayHeight;
+ }
+
public float getDensity() {
return mDensity;
}
@@ -302,6 +324,10 @@ public final class DisplayCutout {
return mScale;
}
+ public float getPhysicalPixelDisplaySizeRatio() {
+ return mPhysicalPixelDisplaySizeRatio;
+ }
+
private boolean hasCutout() {
return !mCutoutSpec.isEmpty();
}
@@ -315,6 +341,9 @@ public final class DisplayCutout {
result = result * 48271 + mCutoutSpec.hashCode();
result = result * 48271 + Integer.hashCode(mRotation);
result = result * 48271 + Float.hashCode(mScale);
+ result = result * 48271 + Float.hashCode(mPhysicalPixelDisplaySizeRatio);
+ result = result * 48271 + Integer.hashCode(mStableDisplayWidth);
+ result = result * 48271 + Integer.hashCode(mStableDisplayHeight);
return result;
}
@@ -326,8 +355,11 @@ public final class DisplayCutout {
if (o instanceof CutoutPathParserInfo) {
CutoutPathParserInfo c = (CutoutPathParserInfo) o;
return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight
+ && mStableDisplayWidth == c.mStableDisplayWidth
+ && mStableDisplayHeight == c.mStableDisplayHeight
&& mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec)
- && mRotation == c.mRotation && mScale == c.mScale;
+ && mRotation == c.mRotation && mScale == c.mScale
+ && mPhysicalPixelDisplaySizeRatio == c.mPhysicalPixelDisplaySizeRatio;
}
return false;
}
@@ -336,10 +368,13 @@ public final class DisplayCutout {
public String toString() {
return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth
+ " displayHeight=" + mDisplayHeight
+ + " stableDisplayHeight=" + mStableDisplayWidth
+ + " stableDisplayHeight=" + mStableDisplayHeight
+ " density={" + mDensity + "}"
+ " cutoutSpec={" + mCutoutSpec + "}"
+ " rotation={" + mRotation + "}"
+ " scale={" + mScale + "}"
+ + " physicalPixelDisplaySizeRatio={" + mPhysicalPixelDisplaySizeRatio + "}"
+ "}";
}
}
@@ -715,8 +750,9 @@ public final class DisplayCutout {
}
}
final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(
- mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(),
- mCutoutPathParserInfo.getDisplayHeight())
+ mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getStableDisplayWidth(),
+ mCutoutPathParserInfo.getStableDisplayHeight(),
+ mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio())
.parse(mCutoutPathParserInfo.getCutoutSpec());
final Path cutoutPath = cutoutSpec.getPath();
@@ -1014,30 +1050,19 @@ public final class DisplayCutout {
* Creates the display cutout according to
* @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
* rectangle-base approximation of the cutout.
- *
* @hide
*/
public static DisplayCutout fromResourcesRectApproximation(Resources res,
- String displayUniqueId, int displayWidth, int displayHeight) {
+ String displayUniqueId, int stableDisplayWidth, int stableDisplayHeight,
+ int displayWidth, int displayHeight) {
return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId),
- getDisplayCutoutApproximationRect(res, displayUniqueId),
- displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
+ getDisplayCutoutApproximationRect(res, displayUniqueId), stableDisplayWidth,
+ stableDisplayHeight, displayWidth, displayHeight,
+ DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
getWaterfallInsets(res, displayUniqueId)).second;
}
/**
- * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
- *
- * @hide
- */
- public static Path pathFromResources(Resources res, String displayUniqueId, int displayWidth,
- int displayHeight) {
- return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId), null,
- displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- getWaterfallInsets(res, displayUniqueId)).first;
- }
-
- /**
* Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
*
* @hide
@@ -1046,8 +1071,8 @@ public final class DisplayCutout {
public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
int displayHeight, float density, Insets waterfallInsets) {
return pathAndDisplayCutoutFromSpec(
- pathSpec, null, displayWidth, displayHeight, density, waterfallInsets)
- .second;
+ pathSpec, null, displayWidth, displayHeight, displayWidth, displayHeight, density,
+ waterfallInsets).second;
}
/**
@@ -1055,6 +1080,8 @@ public final class DisplayCutout {
*
* @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
* @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+ * @param stableDisplayWidth the stable display width.
+ * @param stableDisplayHeight the stable display height.
* @param displayWidth the display width.
* @param displayHeight the display height.
* @param density the display density.
@@ -1062,8 +1089,8 @@ public final class DisplayCutout {
* @return a Pair contains the cutout path and the corresponding DisplayCutout instance.
*/
private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
- String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density,
- Insets waterfallInsets) {
+ String pathSpec, String rectSpec, int stableDisplayWidth, int stableDisplayHeight,
+ int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
// Always use the rect approximation spec to create the cutout if it's not null because
// transforming and sending a Region constructed from a path is very costly.
String spec = rectSpec != null ? rectSpec : pathSpec;
@@ -1071,11 +1098,15 @@ public final class DisplayCutout {
return NULL_PAIR;
}
+ final float physicalPixelDisplaySizeRatio = DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ stableDisplayWidth, stableDisplayHeight, displayWidth, displayHeight);
+
synchronized (CACHE_LOCK) {
if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
&& sCachedDisplayHeight == displayHeight
&& sCachedDensity == density
- && waterfallInsets.equals(sCachedWaterfallInsets)) {
+ && waterfallInsets.equals(sCachedWaterfallInsets)
+ && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
return sCachedCutout;
}
}
@@ -1083,7 +1114,7 @@ public final class DisplayCutout {
spec = spec.trim();
CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(density,
- displayWidth, displayHeight).parse(spec);
+ stableDisplayWidth, stableDisplayHeight, physicalPixelDisplaySizeRatio).parse(spec);
Rect safeInset = cutoutSpec.getSafeInset();
final Rect boundLeft = cutoutSpec.getLeftBound();
final Rect boundTop = cutoutSpec.getTopBound();
@@ -1099,8 +1130,9 @@ public final class DisplayCutout {
Math.max(waterfallInsets.bottom, safeInset.bottom));
}
- final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth,
- displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */);
+ final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(
+ displayWidth, displayHeight, stableDisplayWidth, stableDisplayHeight, density,
+ pathSpec.trim(), ROTATION_0, 1f /* scale */, physicalPixelDisplaySizeRatio);
final DisplayCutout cutout = new DisplayCutout(
safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
@@ -1113,6 +1145,7 @@ public final class DisplayCutout {
sCachedDensity = density;
sCachedCutout = result;
sCachedWaterfallInsets = waterfallInsets;
+ sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
return result;
}
@@ -1149,8 +1182,9 @@ public final class DisplayCutout {
Collections.rotate(Arrays.asList(newBounds), -rotation);
final CutoutPathParserInfo info = getCutoutPathParserInfo();
final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
- info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
- info.getCutoutSpec(), toRotation, info.getScale());
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getStableDisplayWidth(),
+ info.getStableDisplayHeight(), info.getDensity(), info.getCutoutSpec(), toRotation,
+ info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
final boolean swapAspect = (rotation % 2) != 0;
final int endWidth = swapAspect ? startHeight : startWidth;
final int endHeight = swapAspect ? startWidth : startHeight;
@@ -1250,10 +1284,13 @@ public final class DisplayCutout {
out.writeTypedObject(cutout.mWaterfallInsets, flags);
out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth());
out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight());
+ out.writeInt(cutout.mCutoutPathParserInfo.getStableDisplayWidth());
+ out.writeInt(cutout.mCutoutPathParserInfo.getStableDisplayHeight());
out.writeFloat(cutout.mCutoutPathParserInfo.getDensity());
out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec());
out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
+ out.writeFloat(cutout.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
}
}
@@ -1299,12 +1336,16 @@ public final class DisplayCutout {
Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
int displayWidth = in.readInt();
int displayHeight = in.readInt();
+ int stableDisplayWidth = in.readInt();
+ int stableDisplayHeight = in.readInt();
float density = in.readFloat();
String cutoutSpec = in.readString();
int rotation = in.readInt();
float scale = in.readFloat();
+ float physicalPixelDisplaySizeRatio = in.readFloat();
final CutoutPathParserInfo info = new CutoutPathParserInfo(
- displayWidth, displayHeight, density, cutoutSpec, rotation, scale);
+ displayWidth, displayHeight, stableDisplayWidth, stableDisplayHeight, density,
+ cutoutSpec, rotation, scale, physicalPixelDisplaySizeRatio);
return new DisplayCutout(
safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
@@ -1332,10 +1373,13 @@ public final class DisplayCutout {
final CutoutPathParserInfo info = new CutoutPathParserInfo(
mInner.mCutoutPathParserInfo.getDisplayWidth(),
mInner.mCutoutPathParserInfo.getDisplayHeight(),
+ mInner.mCutoutPathParserInfo.getStableDisplayWidth(),
+ mInner.mCutoutPathParserInfo.getStableDisplayHeight(),
mInner.mCutoutPathParserInfo.getDensity(),
mInner.mCutoutPathParserInfo.getCutoutSpec(),
mInner.mCutoutPathParserInfo.getRotation(),
- scale);
+ scale,
+ mInner.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
}
@@ -1387,7 +1431,7 @@ public final class DisplayCutout {
if (mCutoutPath != null) {
// Create a fake CutoutPathParserInfo and set it to sCachedCutoutPathParserInfo so
// that when getCutoutPath() is called, it will return the cached Path.
- info = new CutoutPathParserInfo(0, 0, 0, "test", 0, 1f);
+ info = new CutoutPathParserInfo(0, 0, 0, 0, 0, "test", ROTATION_0, 1f, 1f);
synchronized (CACHE_LOCK) {
DisplayCutout.sCachedCutoutPathParserInfo = info;
DisplayCutout.sCachedCutoutPath = mCutoutPath;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index cc93adc32541..0bed342a8b27 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2280,8 +2280,11 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * {@link #getX(int)} for the first pointer index (may be an
- * arbitrary pointer identifier).
+ * Equivalent to {@link #getX(int)} for pointer index 0 (regardless of the
+ * pointer identifier).
+ *
+ * @return The X coordinate of the first pointer index in the coordinate
+ * space of the view that received this motion event.
*
* @see #AXIS_X
*/
@@ -2290,8 +2293,11 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * {@link #getY(int)} for the first pointer index (may be an
- * arbitrary pointer identifier).
+ * Equivalent to {@link #getY(int)} for pointer index 0 (regardless of the
+ * pointer identifier).
+ *
+ * @return The Y coordinate of the first pointer index in the coordinate
+ * space of the view that received this motion event.
*
* @see #AXIS_Y
*/
@@ -2433,13 +2439,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the X coordinate of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the view that received this motion event.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the X coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #AXIS_X
*/
@@ -2448,13 +2461,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the Y coordinate of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the view that received this motion event.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the Y coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #AXIS_Y
*/
@@ -2700,12 +2720,13 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the original raw X coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Equivalent to {@link #getRawX(int)} for pointer index 0 (regardless of
+ * the pointer identifier).
*
- * @see #getX(int)
+ * @return The X coordinate of the first pointer index in the coordinate
+ * space of the device display.
+ *
+ * @see #getX()
* @see #AXIS_X
*/
public final float getRawX() {
@@ -2713,12 +2734,13 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the original raw Y coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Equivalent to {@link #getRawY(int)} for pointer index 0 (regardless of
+ * the pointer identifier).
*
- * @see #getY(int)
+ * @return The Y coordinate of the first pointer index in the coordinate
+ * space of the device display.
+ *
+ * @see #getY()
* @see #AXIS_Y
*/
public final float getRawY() {
@@ -2726,13 +2748,38 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the original raw X coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
- *
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the device display, irrespective of system
+ * decorations and whether or not the system is in multi-window mode. If the
+ * app spans multiple screens in a multiple-screen environment, the
+ * coordinate space includes all of the spanned screens.
+ *
+ * <p>In multi-window mode, the coordinate space extends beyond the bounds
+ * of the app window to encompass the entire display area. For example, if
+ * the motion event occurs in the right-hand window of split-screen mode in
+ * landscape orientation, the left edge of the screen&mdash;not the left
+ * edge of the window&mdash;is the origin from which the X coordinate is
+ * calculated.
+ *
+ * <p>In multiple-screen scenarios, the coordinate space can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device,
+ * and the motion event occurs on the right-hand screen, the X coordinate is
+ * calculated from the left edge of the left-hand screen to the point of the
+ * motion event on the right-hand screen. When the app is restricted to a
+ * single screen in a multiple-screen environment, the coordinate space
+ * includes only the screen on which the app is running.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the X coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #getX(int)
* @see #AXIS_X
@@ -2742,13 +2789,38 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
- * Returns the original raw Y coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
- *
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the device display, irrespective of system
+ * decorations and whether or not the system is in multi-window mode. If the
+ * app spans multiple screens in a multiple-screen environment, the
+ * coordinate space includes all of the spanned screens.
+ *
+ * <p>In multi-window mode, the coordinate space extends beyond the bounds
+ * of the app window to encompass the entire device screen. For example, if
+ * the motion event occurs in the lower window of split-screen mode in
+ * portrait orientation, the top edge of the screen&mdash;not the top edge
+ * of the window&mdash;is the origin from which the Y coordinate is
+ * determined.
+ *
+ * <p>In multiple-screen scenarios, the coordinate space can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device
+ * that's rotated 90 degrees, and the motion event occurs on the lower
+ * screen, the Y coordinate is calculated from the top edge of the upper
+ * screen to the point of the motion event on the lower screen. When the app
+ * is restricted to a single screen in a multiple-screen environment, the
+ * coordinate space includes only the screen on which the app is running.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the Y coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #getY(int)
* @see #AXIS_Y
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 3eade77a0232..9f30487d5a36 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -67,6 +67,8 @@ public class RoundedCorners implements Parcelable {
private static Pair<Integer, Integer> sCachedRadii;
@GuardedBy("CACHE_LOCK")
private static RoundedCorners sCachedRoundedCorners;
+ @GuardedBy("CACHE_LOCK")
+ private static float sCachedPhysicalPixelDisplaySizeRatio;
@VisibleForTesting
public final RoundedCorner[] mRoundedCorners;
@@ -96,8 +98,10 @@ public class RoundedCorners implements Parcelable {
* @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
*/
public static RoundedCorners fromResources(
- Resources res, String displayUniqueId, int displayWidth, int displayHeight) {
- return fromRadii(loadRoundedCornerRadii(res, displayUniqueId), displayWidth, displayHeight);
+ Resources res, String displayUniqueId, int stableDisplayWidth, int stableDisplayHeight,
+ int displayWidth, int displayHeight) {
+ return fromRadii(loadRoundedCornerRadii(res, displayUniqueId), stableDisplayWidth,
+ stableDisplayHeight, displayWidth, displayHeight);
}
/**
@@ -106,20 +110,33 @@ public class RoundedCorners implements Parcelable {
@VisibleForTesting
public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
int displayHeight) {
+ return fromRadii(radii, displayWidth, displayHeight, displayWidth, displayHeight);
+ }
+
+ private static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int stableDisplayWidth,
+ int stableDisplayHeight, int displayWidth, int displayHeight) {
if (radii == null) {
return null;
}
+ final float physicalPixelDisplaySizeRatio = DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ stableDisplayWidth, stableDisplayHeight, displayWidth, displayHeight);
+
synchronized (CACHE_LOCK) {
if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
- && sCachedDisplayHeight == displayHeight) {
+ && sCachedDisplayHeight == displayHeight
+ && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
return sCachedRoundedCorners;
}
}
final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
- final int topRadius = radii.first > 0 ? radii.first : 0;
- final int bottomRadius = radii.second > 0 ? radii.second : 0;
+ int topRadius = radii.first > 0 ? radii.first : 0;
+ int bottomRadius = radii.second > 0 ? radii.second : 0;
+ if (physicalPixelDisplaySizeRatio != 1f) {
+ topRadius = (int) (topRadius * physicalPixelDisplaySizeRatio + 0.5);
+ bottomRadius = (int) (bottomRadius * physicalPixelDisplaySizeRatio + 0.5);
+ }
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
roundedCorners[i] = createRoundedCorner(
i,
@@ -134,6 +151,7 @@ public class RoundedCorners implements Parcelable {
sCachedDisplayHeight = displayHeight;
sCachedRadii = radii;
sCachedRoundedCorners = result;
+ sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
return result;
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 7cfc983b8e3f..f7bca5bfe188 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -273,8 +273,14 @@ public class SurfaceControlViewHost {
/** @hide */
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm) {
+ this(c, d, wwm, false /* useSfChoreographer */);
+ }
+
+ /** @hide */
+ public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
+ @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
mWm = wwm;
- mViewRoot = new ViewRootImpl(c, d, mWm);
+ mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
addConfigCallback(c, d);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 569461e81111..dc3620bcc5e5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1282,6 +1282,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
/**
+ * A hint indicating that this view can be autofilled with a password.
+ *
+ * This is a heuristic-based hint that is meant to be used by UI Toolkit developers when a
+ * view is a password field but doesn't specify a
+ * <code>{@value View#AUTOFILL_HINT_PASSWORD}</code>.
+ * @hide
+ */
+ // TODO(229765029): unhide this for UI toolkit
+ public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";
+
+ /**
* Hints for the autofill services that describes the content of the view.
*/
private @Nullable String[] mAutofillHints;
@@ -8207,12 +8218,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
} else {
- afm.enableFillRequestActivityStarted();
+ afm.enableFillRequestActivityStarted(this);
}
} else if (!enter && !isFocused()) {
afm.notifyViewExited(this);
} else if (enter) {
- afm.enableFillRequestActivityStarted();
+ afm.enableFillRequestActivityStarted(this);
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 27ce71155857..fbb86ff3a55a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -854,10 +854,16 @@ public final class ViewRootImpl implements ViewParent,
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
- this(context, display, WindowManagerGlobal.getWindowSession());
+ this(context, display, WindowManagerGlobal.getWindowSession(),
+ false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
+ this(context, display, session, false /* useSfChoreographer */);
+ }
+
+ public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
+ boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
mDisplay = display;
@@ -889,7 +895,8 @@ public final class ViewRootImpl implements ViewParent,
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
// TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
- mChoreographer = Choreographer.getInstance();
+ mChoreographer = useSfChoreographer
+ ? Choreographer.getSfInstance() : Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration,
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index b05b7916d960..0a75992811f4 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -488,6 +488,25 @@ public final class AutofillManager {
public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED =
"autofill_dialog_enabled";
+ /**
+ * Sets the autofill hints allowed list for the fields that can trigger the fill dialog
+ * feature at Activity starting.
+ *
+ * The list of autofill hints is {@code ":"} colon delimited.
+ *
+ * <p>For example, a list with 3 hints {@code password}, {@code phone}, and
+ * {@code emailAddress}, would be {@code password:phone:emailAddress}
+ *
+ * Note: By default the password field is enabled even there is no password hint in the list
+ *
+ * @see View#setAutofillHints(String...)
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS =
+ "autofill_dialog_hints";
+
+ private static final String DIALOG_HINTS_DELIMITER = ":";
+
/** @hide */
public static final int RESULT_OK = 0;
/** @hide */
@@ -537,6 +556,7 @@ public final class AutofillManager {
public static final int NO_SESSION = Integer.MAX_VALUE;
private static final boolean HAS_FILL_DIALOG_UI_FEATURE_DEFAULT = false;
+ private static final String FILL_DIALOG_ENABLED_DEFAULT_HINTS = "";
private final IAutoFillManager mService;
@@ -652,6 +672,8 @@ public final class AutofillManager {
// Indicates whether called the showAutofillDialog() method.
private boolean mShowAutofillDialogCalled = false;
+ private final String[] mFillDialogEnabledHints;
+
/** @hide */
public interface AutofillClient {
/**
@@ -796,8 +818,10 @@ public final class AutofillManager {
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED,
HAS_FILL_DIALOG_UI_FEATURE_DEFAULT);
+ mFillDialogEnabledHints = getFillDialogEnabledHints();
if (sDebug) {
- Log.d(TAG, "Fill dialog is enabled:" + mIsFillDialogEnabled);
+ Log.d(TAG, "Fill dialog is enabled:" + mIsFillDialogEnabled
+ + ", hints=" + Arrays.toString(mFillDialogEnabledHints));
}
if (mOptions != null) {
@@ -806,6 +830,19 @@ public final class AutofillManager {
}
}
+ private String[] getFillDialogEnabledHints() {
+ final String dialogHints = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS,
+ FILL_DIALOG_ENABLED_DEFAULT_HINTS);
+ if (TextUtils.isEmpty(dialogHints)) {
+ return new String[0];
+ }
+
+ return ArrayUtils.filter(dialogHints.split(DIALOG_HINTS_DELIMITER), String[]::new,
+ (str) -> !TextUtils.isEmpty(str));
+ }
+
/**
* @hide
*/
@@ -1076,17 +1113,27 @@ public final class AutofillManager {
}
/**
- * The view is autofillable, marked to perform a fill request after layout if
+ * The view have the allowed autofill hints, marked to perform a fill request after layout if
* the field does not trigger a fill request.
*
* @hide
*/
- public void enableFillRequestActivityStarted() {
- mRequireAutofill = true;
+ public void enableFillRequestActivityStarted(View v) {
+ if (mRequireAutofill) {
+ return;
+ }
+
+ if (mIsFillDialogEnabled
+ || ArrayUtils.containsAny(v.getAutofillHints(), mFillDialogEnabledHints)) {
+ if (sDebug) {
+ Log.d(TAG, "Trigger fill request at starting");
+ }
+ mRequireAutofill = true;
+ }
}
private boolean hasFillDialogUiFeature() {
- return mIsFillDialogEnabled;
+ return mIsFillDialogEnabled || !ArrayUtils.isEmpty(mFillDialogEnabledHints);
}
/**
@@ -1911,7 +1958,7 @@ public final class AutofillManager {
if (newClientState != null) {
responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
}
- if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ if (data.hasExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
false));
@@ -2977,6 +3024,8 @@ public final class AutofillManager {
pw.print(pfx); pw.print("compat mode enabled: ");
synchronized (mLock) {
pw.print(pfx); pw.print("fill dialog enabled: "); pw.println(mIsFillDialogEnabled);
+ pw.print(pfx); pw.print("fill dialog enabled hints: ");
+ pw.println(Arrays.toString(mFillDialogEnabledHints));
if (mCompatibilityBridge != null) {
final String pfx2 = pfx + " ";
pw.println("true");
diff --git a/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java
new file mode 100644
index 000000000000..240e38b107a5
--- /dev/null
+++ b/core/java/android/view/inputmethod/IAccessibilityInputMethodSessionInvoker.java
@@ -0,0 +1,84 @@
+/*
+ * 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.view.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+
+final class IAccessibilityInputMethodSessionInvoker {
+ private static final String TAG = "IAccessibilityInputMethodSessionInvoker";
+
+ /**
+ * The actual instance of the method to make calls on it.
+ */
+ @NonNull
+ private final IAccessibilityInputMethodSession mSession;
+
+ private IAccessibilityInputMethodSessionInvoker(
+ @NonNull IAccessibilityInputMethodSession session) {
+ mSession = session;
+ }
+
+ /**
+ * Create a {@link IAccessibilityInputMethodSessionInvoker} instance if applicable.
+ *
+ * @param session {@link IAccessibilityInputMethodSession} object to be wrapped.
+ * @return an instance of {@link IAccessibilityInputMethodSessionInvoker} if
+ * {@code inputMethodSession} is not {@code null}. {@code null} otherwise.
+ */
+ @Nullable
+ public static IAccessibilityInputMethodSessionInvoker createOrNull(
+ @NonNull IAccessibilityInputMethodSession session) {
+ return session == null ? null : new IAccessibilityInputMethodSessionInvoker(session);
+ }
+
+ @AnyThread
+ void finishInput() {
+ try {
+ mSession.finishInput();
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
+ int candidatesStart, int candidatesEnd) {
+ try {
+ mSession.updateSelection(
+ oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+
+ @AnyThread
+ void invalidateInput(EditorInfo editorInfo, IRemoteAccessibilityInputConnection connection,
+ int sessionId) {
+ try {
+ mSession.invalidateInput(editorInfo, connection, sessionId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "A11yIME died", e);
+ }
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 805f8e7551a5..84f13930e03a 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -93,6 +93,7 @@ import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -506,8 +507,8 @@ public final class InputMethodManager {
*/
@Nullable
@GuardedBy("mH")
- private final SparseArray<InputMethodSessionWrapper> mAccessibilityInputMethodSession =
- new SparseArray<>();
+ private final SparseArray<IAccessibilityInputMethodSessionInvoker>
+ mAccessibilityInputMethodSession = new SparseArray<>();
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
@@ -669,7 +670,8 @@ public final class InputMethodManager {
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.finishInput();
}
- forAccessibilitySessions(InputMethodSessionWrapper::finishInput);
+ forAccessibilitySessionsLocked(
+ IAccessibilityInputMethodSessionInvoker::finishInput);
}
}
@@ -730,7 +732,7 @@ public final class InputMethodManager {
focusedView.getWindowToken(), startInputFlags, softInputMode,
windowFlags,
null,
- null,
+ null, null,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -963,13 +965,13 @@ public final class InputMethodManager {
// we send a notification so that the a11y service knows the session is
// registered and update the a11y service with the current cursor positions.
if (res.accessibilitySessions != null) {
- InputMethodSessionWrapper wrapper =
- InputMethodSessionWrapper.createOrNull(
+ IAccessibilityInputMethodSessionInvoker invoker =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
res.accessibilitySessions.get(id));
- if (wrapper != null) {
- mAccessibilityInputMethodSession.put(id, wrapper);
+ if (invoker != null) {
+ mAccessibilityInputMethodSession.put(id, invoker);
if (mServedInputConnection != null) {
- wrapper.updateSelection(mInitialSelStart, mInitialSelEnd,
+ invoker.updateSelection(mInitialSelStart, mInitialSelEnd,
mCursorSelStart, mCursorSelEnd, mCursorCandStart,
mCursorCandEnd);
} else {
@@ -978,9 +980,7 @@ public final class InputMethodManager {
// binds before or after input starts, it may wonder if it binds
// after input starts, why it doesn't receive a notification of
// the current cursor positions.
- wrapper.updateSelection(-1, -1,
- -1, -1, -1,
- -1);
+ invoker.updateSelection(-1, -1, -1, -1, -1, -1);
}
}
}
@@ -2148,8 +2148,10 @@ public final class InputMethodManager {
editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText());
mCurrentInputMethodSession.invalidateInput(editorInfo, mServedInputConnection,
sessionId);
- forAccessibilitySessions(wrapper -> wrapper.invalidateInput(editorInfo,
- mServedInputConnection, sessionId));
+ final IRemoteAccessibilityInputConnection accessibilityInputConnection =
+ mServedInputConnection.asIRemoteAccessibilityInputConnection();
+ forAccessibilitySessionsLocked(wrapper -> wrapper.invalidateInput(editorInfo,
+ accessibilityInputConnection, sessionId));
return true;
}
}
@@ -2323,6 +2325,8 @@ public final class InputMethodManager {
res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
softInputMode, windowFlags, tba, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2347,8 +2351,9 @@ public final class InputMethodManager {
mAccessibilityInputMethodSession.clear();
if (res.accessibilitySessions != null) {
for (int i = 0; i < res.accessibilitySessions.size(); i++) {
- InputMethodSessionWrapper wrapper = InputMethodSessionWrapper.createOrNull(
- res.accessibilitySessions.valueAt(i));
+ IAccessibilityInputMethodSessionInvoker wrapper =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
+ res.accessibilitySessions.valueAt(i));
if (wrapper != null) {
mAccessibilityInputMethodSession.append(
res.accessibilitySessions.keyAt(i), wrapper);
@@ -2598,7 +2603,7 @@ public final class InputMethodManager {
mCursorCandEnd = candidatesEnd;
mCurrentInputMethodSession.updateSelection(
oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
- forAccessibilitySessions(wrapper -> wrapper.updateSelection(oldSelStart,
+ forAccessibilitySessionsLocked(wrapper -> wrapper.updateSelection(oldSelStart,
oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd));
}
}
@@ -3658,7 +3663,8 @@ public final class InputMethodManager {
}
}
- private void forAccessibilitySessions(Consumer<InputMethodSessionWrapper> consumer) {
+ private void forAccessibilitySessionsLocked(
+ Consumer<IAccessibilityInputMethodSessionInvoker> consumer) {
for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
consumer.accept(mAccessibilityInputMethodSession.valueAt(i));
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index fbad38f27a23..2879cd888d2d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1588,24 +1588,33 @@ public class RemoteViews implements Parcelable, Filter {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ArrayList<Bitmap> mBitmaps;
+ SparseIntArray mBitmapHashes;
int mBitmapMemory = -1;
public BitmapCache() {
mBitmaps = new ArrayList<>();
+ mBitmapHashes = new SparseIntArray();
}
public BitmapCache(Parcel source) {
mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
+ mBitmapHashes = source.readSparseIntArray();
}
public int getBitmapId(Bitmap b) {
if (b == null) {
return -1;
} else {
- if (mBitmaps.contains(b)) {
- return mBitmaps.indexOf(b);
+ int hash = b.hashCode();
+ int hashId = mBitmapHashes.get(hash, -1);
+ if (hashId != -1) {
+ return hashId;
} else {
+ if (b.isMutable()) {
+ b = b.asShared();
+ }
mBitmaps.add(b);
+ mBitmapHashes.put(mBitmaps.size() - 1, hash);
mBitmapMemory = -1;
return (mBitmaps.size() - 1);
}
@@ -1616,13 +1625,13 @@ public class RemoteViews implements Parcelable, Filter {
public Bitmap getBitmapForId(int id) {
if (id == -1 || id >= mBitmaps.size()) {
return null;
- } else {
- return mBitmaps.get(id);
}
+ return mBitmaps.get(id);
}
public void writeBitmapsToParcel(Parcel dest, int flags) {
dest.writeTypedList(mBitmaps, flags);
+ dest.writeSparseIntArray(mBitmapHashes);
}
public int getBitmapMemory() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 54c0d7c9af32..93f72640bd3f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6974,6 +6974,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mEditor.mInputType = type;
}
+ @Override
+ public String[] getAutofillHints() {
+ String[] hints = super.getAutofillHints();
+ if (isAnyPasswordInputType()) {
+ if (!ArrayUtils.contains(hints, AUTOFILL_HINT_PASSWORD_AUTO)) {
+ hints = ArrayUtils.appendElement(String.class, hints,
+ AUTOFILL_HINT_PASSWORD_AUTO);
+ }
+ }
+ return hints;
+ }
+
/**
* @return {@code null} if the key listener should use pre-O (locale-independent). Otherwise
* a {@code Locale} object that can be used to customize key various listeners.
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index f2db5648a75f..79dac19d0927 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -396,8 +396,6 @@ public final class WindowContainerTransaction implements Parcelable {
/**
* Sets the container as launch adjacent flag root. Task starting with
* {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to.
- *
- * @hide
*/
@NonNull
public WindowContainerTransaction setLaunchAdjacentFlagRoot(
@@ -409,8 +407,6 @@ public final class WindowContainerTransaction implements Parcelable {
/**
* Clears launch adjacent flag root for the display area of passing container.
- *
- * @hide
*/
@NonNull
public WindowContainerTransaction clearLaunchAdjacentFlagRoot(
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index f8484d15344e..2d2c8de72646 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -165,10 +165,11 @@ public abstract class WindowProviderService extends Service implements WindowPro
}
}
+ // Suppress the lint because ths is overridden from Context.
@SuppressLint("OnNameExpected")
@Override
- // Suppress the lint because ths is overridden from Context.
- public @Nullable Object getSystemService(@NonNull String name) {
+ @Nullable
+ public Object getSystemService(@NonNull String name) {
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 547535d90e5a..b0be0df80b29 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -91,7 +91,6 @@ public class WindowTokenClient extends IWindowToken.Stub {
throw new IllegalStateException("Context is already attached.");
}
mContextRef = new WeakReference<>(context);
- mConfiguration.setTo(context.getResources().getConfiguration());
mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
&& context instanceof AbstractInputMethodService;
}
@@ -112,7 +111,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
if (configuration == null) {
return false;
}
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mHandler.runWithScissors(() -> onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */), 0 /* timeout */);
mAttachToWindowContainer = true;
return true;
} catch (RemoteException e) {
@@ -137,8 +137,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
if (configuration == null) {
return false;
}
- mHandler.post(() -> onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */));
+ mHandler.runWithScissors(() -> onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */), 0 /* timeout */);
mAttachToWindowContainer = true;
return true;
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index d4a8a164803f..49ee91c4c2a7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1616,29 +1616,48 @@ public class ChooserActivity extends ResolverActivity implements
return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true);
}
- private void showTargetDetails(DisplayResolveInfo ti) {
- if (ti == null) return;
+ private void showTargetDetails(TargetInfo targetInfo) {
+ if (targetInfo == null) return;
ArrayList<DisplayResolveInfo> targetList;
-
- // For multiple targets, include info on all targets
- if (ti instanceof MultiDisplayResolveInfo) {
- MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
+ ChooserTargetActionsDialogFragment fragment = new ChooserTargetActionsDialogFragment();
+ Bundle bundle = new Bundle();
+
+ if (targetInfo instanceof SelectableTargetInfo) {
+ SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo;
+ if (selectableTargetInfo.getDisplayResolveInfo() == null
+ || selectableTargetInfo.getChooserTarget() == null) {
+ Log.e(TAG, "displayResolveInfo or chooserTarget in selectableTargetInfo are null");
+ return;
+ }
+ targetList = new ArrayList<>();
+ targetList.add(selectableTargetInfo.getDisplayResolveInfo());
+ bundle.putString(ChooserTargetActionsDialogFragment.SHORTCUT_ID_KEY,
+ selectableTargetInfo.getChooserTarget().getIntentExtras().getString(
+ Intent.EXTRA_SHORTCUT_ID));
+ bundle.putBoolean(ChooserTargetActionsDialogFragment.IS_SHORTCUT_PINNED_KEY,
+ selectableTargetInfo.isPinned());
+ bundle.putParcelable(ChooserTargetActionsDialogFragment.INTENT_FILTER_KEY,
+ getTargetIntentFilter());
+ if (selectableTargetInfo.getDisplayLabel() != null) {
+ bundle.putString(ChooserTargetActionsDialogFragment.SHORTCUT_TITLE_KEY,
+ selectableTargetInfo.getDisplayLabel().toString());
+ }
+ } else if (targetInfo instanceof MultiDisplayResolveInfo) {
+ // For multiple targets, include info on all targets
+ MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
targetList = mti.getTargets();
} else {
targetList = new ArrayList<DisplayResolveInfo>();
- targetList.add(ti);
+ targetList.add((DisplayResolveInfo) targetInfo);
}
-
- ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
- Bundle b = new Bundle();
- b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+ bundle.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
- b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+ bundle.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
targetList);
- f.setArguments(b);
+ fragment.setArguments(bundle);
- f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
+ fragment.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
}
private void modifyTargetIntent(Intent in) {
@@ -2864,8 +2883,10 @@ public class ChooserActivity extends ResolverActivity implements
private boolean shouldShowTargetDetails(TargetInfo ti) {
ComponentName nearbyShare = getNearbySharingComponent();
// Suppress target details for nearby share to hide pin/unpin action
- return !(nearbyShare != null && nearbyShare.equals(ti.getResolvedComponentName())
- && shouldNearbyShareBeFirstInRankedRow());
+ boolean isNearbyShare = nearbyShare != null && nearbyShare.equals(
+ ti.getResolvedComponentName()) && shouldNearbyShareBeFirstInRankedRow();
+ return ti instanceof SelectableTargetInfo
+ || (ti instanceof DisplayResolveInfo && !isNearbyShare);
}
/**
@@ -2962,8 +2983,6 @@ public class ChooserActivity extends ResolverActivity implements
private DirectShareViewHolder mDirectShareViewHolder;
private int mChooserTargetWidth = 0;
private boolean mShowAzLabelIfPoss;
-
- private boolean mHideContentPreview = false;
private boolean mLayoutRequested = false;
private int mFooterHeight = 0;
@@ -3034,7 +3053,6 @@ public class ChooserActivity extends ResolverActivity implements
* personal and work tabs.
*/
public void hideContentPreview() {
- mHideContentPreview = true;
mLayoutRequested = true;
notifyDataSetChanged();
}
@@ -3224,18 +3242,15 @@ public class ChooserActivity extends ResolverActivity implements
}
});
- // Direct Share targets should not show any menu
- if (!isDirectShare) {
- v.setOnLongClickListener(v1 -> {
- final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
- holder.getItemIndex(column), true);
- // This should always be the case for non-DS targets, check for validity
- if (ti instanceof DisplayResolveInfo && shouldShowTargetDetails(ti)) {
- showTargetDetails((DisplayResolveInfo) ti);
- }
- return true;
- });
- }
+ // Show menu for both direct share and app share targets after long click.
+ v.setOnLongClickListener(v1 -> {
+ TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
+ holder.getItemIndex(column), true);
+ if (shouldShowTargetDetails(ti)) {
+ showTargetDetails(ti);
+ }
+ return true;
+ });
holder.addView(i, v);
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 140fabc4f268..f6445849d897 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -74,6 +74,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
/** {@link #getBaseScore} */
public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f;
+ private static final float PINNED_SHORTCUT_TARGET_SCORE_BOOST = 1000.f;
private final int mMaxShortcutTargetsPerApp;
private final ChooserListCommunicator mChooserListCommunicator;
@@ -275,8 +276,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background);
holder.text.setPaddingRelative(0, 0, bkg.getIntrinsicWidth() /* end */, 0);
holder.text.setBackground(bkg);
- } else if (info.isPinned() && getPositionTargetType(position) == TARGET_STANDARD) {
- // If the target is pinned and in the suggested row show a pinned indicator
+ } else if (info.isPinned() && (getPositionTargetType(position) == TARGET_STANDARD
+ || getPositionTargetType(position) == TARGET_SERVICE)) {
+ // If the appShare or directShare target is pinned and in the suggested row show a
+ // pinned indicator
Drawable bkg = mContext.getDrawable(R.drawable.chooser_pinned_background);
holder.text.setPaddingRelative(bkg.getIntrinsicWidth() /* start */, 0, 0, 0);
holder.text.setBackground(bkg);
@@ -523,11 +526,16 @@ public class ChooserListAdapter extends ResolverListAdapter {
targetScore = lastScore * 0.95f;
}
}
+ ShortcutInfo shortcutInfo = isShortcutResult ? directShareToShortcutInfos.get(target)
+ : null;
+ if ((shortcutInfo != null) && shortcutInfo.isPinned()) {
+ targetScore += PINNED_SHORTCUT_TARGET_SCORE_BOOST;
+ }
UserHandle userHandle = getUserHandle();
Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */);
boolean isInserted = insertServiceTarget(new SelectableTargetInfo(contextAsUser,
origTarget, target, targetScore, mSelectableTargetInfoCommunicator,
- (isShortcutResult ? directShareToShortcutInfos.get(target) : null)));
+ shortcutInfo));
if (isInserted && isShortcutResult) {
mNumShortcutResults++;
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 9afc0e9e97b0..4f1f380c3a77 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -29,9 +29,14 @@ import android.app.ActivityManager;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -51,6 +56,7 @@ import com.android.internal.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
/**
* Shows a dialog with actions to take on a chooser target.
@@ -60,9 +66,17 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
protected UserHandle mUserHandle;
+ protected String mShortcutId;
+ protected String mShortcutTitle;
+ protected boolean mIsShortcutPinned;
+ protected IntentFilter mIntentFilter;
public static final String USER_HANDLE_KEY = "user_handle";
public static final String TARGET_INFOS_KEY = "target_infos";
+ public static final String SHORTCUT_ID_KEY = "shortcut_id";
+ public static final String SHORTCUT_TITLE_KEY = "shortcut_title";
+ public static final String IS_SHORTCUT_PINNED_KEY = "is_shortcut_pinned";
+ public static final String INTENT_FILTER_KEY = "intent_filter";
public ChooserTargetActionsDialogFragment() {}
@@ -79,6 +93,10 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
void setStateFromBundle(Bundle b) {
mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+ mShortcutId = b.getString(SHORTCUT_ID_KEY);
+ mShortcutTitle = b.getString(SHORTCUT_TITLE_KEY);
+ mIsShortcutPinned = b.getBoolean(IS_SHORTCUT_PINNED_KEY);
+ mIntentFilter = (IntentFilter) b.get(INTENT_FILTER_KEY);
}
@Override
@@ -89,6 +107,11 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
mUserHandle);
outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
mTargetInfos);
+ outState.putString(ChooserTargetActionsDialogFragment.SHORTCUT_ID_KEY, mShortcutId);
+ outState.putBoolean(ChooserTargetActionsDialogFragment.IS_SHORTCUT_PINNED_KEY,
+ mIsShortcutPinned);
+ outState.putString(ChooserTargetActionsDialogFragment.SHORTCUT_TITLE_KEY, mShortcutTitle);
+ outState.putParcelable(ChooserTargetActionsDialogFragment.INTENT_FILTER_KEY, mIntentFilter);
}
/**
@@ -121,7 +144,7 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
RecyclerView rv = v.findViewById(R.id.listContainer);
final ResolveInfoPresentationGetter pg = getProvidingAppPresentationGetter();
- title.setText(pg.getLabel());
+ title.setText(isShortcutTarget() ? mShortcutTitle : pg.getLabel());
icon.setImageDrawable(pg.getIcon(mUserHandle));
rv.setAdapter(new VHAdapter(items));
@@ -180,11 +203,45 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
@Override
public void onClick(DialogInterface dialog, int which) {
- pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+ if (isShortcutTarget()) {
+ toggleShortcutPinned(mTargetInfos.get(which).getResolvedComponentName());
+ } else {
+ pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+ }
((ChooserActivity) getActivity()).handlePackagesChanged();
dismiss();
}
+ private void toggleShortcutPinned(ComponentName name) {
+ if (mIntentFilter == null) {
+ return;
+ }
+ // Fetch existing pinned shortcuts of the given package.
+ List<String> pinnedShortcuts = getPinnedShortcutsFromPackageAsUser(getContext(),
+ mUserHandle, mIntentFilter, name.getPackageName());
+ // If the shortcut has already been pinned, unpin it; otherwise, pin it.
+ if (mIsShortcutPinned) {
+ pinnedShortcuts.remove(mShortcutId);
+ } else {
+ pinnedShortcuts.add(mShortcutId);
+ }
+ // Update pinned shortcut list in ShortcutService via LauncherApps
+ getContext().getSystemService(LauncherApps.class).pinShortcuts(
+ name.getPackageName(), pinnedShortcuts, mUserHandle);
+ }
+
+ private static List<String> getPinnedShortcutsFromPackageAsUser(Context context,
+ UserHandle user, IntentFilter filter, String packageName) {
+ Context contextAsUser = context.createContextAsUser(user, 0 /* flags */);
+ List<ShortcutManager.ShareShortcutInfo> targets = contextAsUser.getSystemService(
+ ShortcutManager.class).getShareTargets(filter);
+ return targets.stream()
+ .map(ShortcutManager.ShareShortcutInfo::getShortcutInfo)
+ .filter(s -> s.isPinned() && s.getPackage().equals(packageName))
+ .map(ShortcutInfo::getId)
+ .collect(Collectors.toList());
+ }
+
private void pinComponent(ComponentName name) {
SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
final String key = name.flattenToString();
@@ -211,12 +268,13 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
@NonNull
protected CharSequence getItemLabel(DisplayResolveInfo dri) {
final PackageManager pm = getContext().getPackageManager();
- return getPinLabel(dri.isPinned(), dri.getResolveInfo().loadLabel(pm));
+ return getPinLabel(isPinned(dri),
+ isShortcutTarget() ? "" : dri.getResolveInfo().loadLabel(pm));
}
@Nullable
protected Drawable getItemIcon(DisplayResolveInfo dri) {
- return getPinIcon(dri.isPinned());
+ return getPinIcon(isPinned(dri));
}
private ResolveInfoPresentationGetter getProvidingAppPresentationGetter() {
@@ -229,4 +287,11 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
mTargetInfos.get(0).getResolveInfo());
}
+ private boolean isPinned(DisplayResolveInfo dri) {
+ return isShortcutTarget() ? mIsShortcutPinned : dri.isPinned();
+ }
+
+ private boolean isShortcutTarget() {
+ return mShortcutId != null;
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserUtil.java b/core/java/com/android/internal/app/ChooserUtil.java
deleted file mode 100644
index 3f8788cba9b9..000000000000
--- a/core/java/com/android/internal/app/ChooserUtil.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.app;
-
-import java.nio.charset.Charset;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Utility method for common computation operations for Share sheet.
- */
-public class ChooserUtil {
-
- private static final Charset UTF_8 = Charset.forName("UTF-8");
-
- /**
- * Hashes the given input based on MD5 algorithm.
- *
- * @return a string representation of the hash computation.
- */
- public static String md5(String input) {
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- md.update(input.getBytes(UTF_8));
- return convertBytesToHexString(md.digest());
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException(e);
- }
- }
-
- /** Converts byte array input into an hex string. */
- private static String convertBytesToHexString(byte[] input) {
- char[] chars = new char[input.length * 2];
- for (int i = 0; i < input.length; i++) {
- byte b = input[i];
- chars[i * 2] = Character.forDigit((b >> 4) & 0xF, 16 /* radix */);
- chars[i * 2 + 1] = Character.forDigit(b & 0xF, 16 /* radix */);
- }
- return new String(chars);
- }
-
- private ChooserUtil() {}
-}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 43dacd72db37..354eb62ba045 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -71,7 +71,7 @@ public class SimpleIconFactory {
new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
- private static final float BLUR_FACTOR = 0.5f / 48;
+ private static final float BLUR_FACTOR = 1.5f / 48;
private Context mContext;
private Canvas mCanvas;
@@ -650,8 +650,8 @@ public class SimpleIconFactory {
/* Shadow generator block */
private static final float KEY_SHADOW_DISTANCE = 1f / 48;
- private static final int KEY_SHADOW_ALPHA = 61;
- private static final int AMBIENT_SHADOW_ALPHA = 30;
+ private static final int KEY_SHADOW_ALPHA = 10;
+ private static final int AMBIENT_SHADOW_ALPHA = 7;
private Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 015336388b5d..264e4f76d35d 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -64,6 +64,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
private Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
+ private final boolean mIsPinned;
private final float mModifiedScore;
private boolean mIsSuspended = false;
@@ -77,6 +78,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
mModifiedScore = modifiedScore;
mPm = mContext.getPackageManager();
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
+ mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
if (sourceInfo != null) {
final ResolveInfo ri = sourceInfo.getResolveInfo();
if (ri != null) {
@@ -120,6 +122,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
+ mIsPinned = other.mIsPinned;
mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle());
}
@@ -292,7 +295,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
@Override
public boolean isPinned() {
- return mSourceInfo != null && mSourceInfo.isPinned();
+ return mIsPinned;
}
/**
diff --git a/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl
new file mode 100644
index 000000000000..ccfe3fb014b4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSession.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.inputmethod;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.view.IInputContext;
+
+/**
+ * Sub-interface of IInputMethodSession which is safe to give to A11y IME.
+ */
+oneway interface IAccessibilityInputMethodSession {
+ void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd);
+
+ void finishInput();
+
+ void finishSession();
+
+ void invalidateInput(in EditorInfo editorInfo,
+ in IRemoteAccessibilityInputConnection connection, int sessionId);
+}
diff --git a/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl
index 8fbdefe5931e..bb42c60271fd 100644
--- a/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl
+++ b/core/java/com/android/internal/inputmethod/IAccessibilityInputMethodSessionCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
- package com.android.internal.view;
+ package com.android.internal.inputmethod;
- import com.android.internal.view.IInputMethodSession;
+ import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
/**
* Helper interface for IInputMethod to allow the input method to notify the client when a new
* session has been created.
*/
-oneway interface IInputSessionWithIdCallback {
- void sessionCreated(IInputMethodSession session, int id);
-} \ No newline at end of file
+oneway interface IAccessibilityInputMethodSessionCallback {
+ void sessionCreated(IAccessibilityInputMethodSession session, int id);
+}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl
new file mode 100644
index 000000000000..f2064bb93e0c
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnection.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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.inputmethod;
+
+import android.view.KeyEvent;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.inputmethod.InputConnectionCommandHeader;
+
+/**
+ * Interface from A11y IMEs to the application, allowing it to perform edits on the current input
+ * field and other interactions with the application.
+ */
+oneway interface IRemoteAccessibilityInputConnection {
+ void commitText(in InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, in TextAttribute textAttribute);
+
+ void setSelection(in InputConnectionCommandHeader header, int start, int end);
+
+ void getSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+ int afterLength, int flags, in AndroidFuture future /* T=SurroundingText */);
+
+ void deleteSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+ int afterLength);
+
+ void sendKeyEvent(in InputConnectionCommandHeader header, in KeyEvent event);
+
+ void performEditorAction(in InputConnectionCommandHeader header, int actionCode);
+
+ void performContextMenuAction(in InputConnectionCommandHeader header, int id);
+
+ void getCursorCapsMode(in InputConnectionCommandHeader header, int reqModes,
+ in AndroidFuture future /* T=Integer */);
+
+ void clearMetaKeyStates(in InputConnectionCommandHeader header, int states);
+}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java
new file mode 100644
index 000000000000..dcc67d49251f
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IRemoteAccessibilityInputConnectionInvoker.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.Objects;
+
+final class IRemoteAccessibilityInputConnectionInvoker {
+ @NonNull
+ private final IRemoteAccessibilityInputConnection mConnection;
+ private final int mSessionId;
+
+ private IRemoteAccessibilityInputConnectionInvoker(
+ @NonNull IRemoteAccessibilityInputConnection inputContext, int sessionId) {
+ mConnection = inputContext;
+ mSessionId = sessionId;
+ }
+
+ /**
+ * Creates a new instance of {@link IRemoteAccessibilityInputConnectionInvoker} for the given
+ * {@link IRemoteAccessibilityInputConnection}.
+ *
+ * @param connection {@link IRemoteAccessibilityInputConnection} to be wrapped.
+ * @return A new instance of {@link IRemoteAccessibilityInputConnectionInvoker}.
+ */
+ public static IRemoteAccessibilityInputConnectionInvoker create(
+ @NonNull IRemoteAccessibilityInputConnection connection) {
+ Objects.requireNonNull(connection);
+ return new IRemoteAccessibilityInputConnectionInvoker(connection, 0);
+ }
+
+ /**
+ * Creates a new instance of {@link IRemoteAccessibilityInputConnectionInvoker} with the given
+ * {@code sessionId}.
+ *
+ * @param sessionId the new session ID to be used.
+ * @return A new instance of {@link IRemoteAccessibilityInputConnectionInvoker}.
+ */
+ @NonNull
+ public IRemoteAccessibilityInputConnectionInvoker cloneWithSessionId(int sessionId) {
+ return new IRemoteAccessibilityInputConnectionInvoker(mConnection, sessionId);
+ }
+
+ /**
+ * @param connection {@code IRemoteAccessibilityInputConnection} to be compared with
+ * @return {@code true} if the underlying {@code IRemoteAccessibilityInputConnection} is the
+ * same. {@code false} if {@code connection} is {@code null}.
+ */
+ @AnyThread
+ public boolean isSameConnection(@NonNull IRemoteAccessibilityInputConnection connection) {
+ if (connection == null) {
+ return false;
+ }
+ return mConnection.asBinder() == connection.asBinder();
+ }
+
+ @NonNull
+ InputConnectionCommandHeader createHeader() {
+ return new InputConnectionCommandHeader(mSessionId);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#commitText(InputConnectionCommandHeader,
+ * int, CharSequence)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ */
+ @AnyThread
+ public void commitText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mConnection.commitText(createHeader(), text, newCursorPosition, textAttribute);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#setSelection(InputConnectionCommandHeader,
+ * int, int)}.
+ *
+ * @param start {@code start} parameter to be passed.
+ * @param end {@code start} parameter to be passed.
+ */
+ @AnyThread
+ public void setSelection(int start, int end) {
+ try {
+ mConnection.setSelection(createHeader(), start, end);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getSurroundingText(
+ * InputConnectionCommandHeader, int, int, int, AndroidFuture)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link AndroidFuture< SurroundingText >} that can be used to retrieve the
+ * invocation result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public AndroidFuture<SurroundingText> getSurroundingText(int beforeLength, int afterLength,
+ int flags) {
+ final AndroidFuture<SurroundingText> future = new AndroidFuture<>();
+ try {
+ mConnection.getSurroundingText(createHeader(), beforeLength, afterLength, flags,
+ future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ return future;
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#deleteSurroundingText(
+ * InputConnectionCommandHeader, int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ */
+ @AnyThread
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ try {
+ mConnection.deleteSurroundingText(createHeader(), beforeLength, afterLength);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#sendKeyEvent(
+ * InputConnectionCommandHeader, KeyEvent)}.
+ *
+ * @param event {@code event} parameter to be passed.
+ */
+ @AnyThread
+ public void sendKeyEvent(KeyEvent event) {
+ try {
+ mConnection.sendKeyEvent(createHeader(), event);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performEditorAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param actionCode {@code start} parameter to be passed.
+ */
+ @AnyThread
+ public void performEditorAction(int actionCode) {
+ try {
+ mConnection.performEditorAction(createHeader(), actionCode);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performContextMenuAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param id {@code id} parameter to be passed.
+ */
+ @AnyThread
+ public void performContextMenuAction(int id) {
+ try {
+ mConnection.performContextMenuAction(createHeader(), id);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getCursorCapsMode(
+ * InputConnectionCommandHeader, int, AndroidFuture)}.
+ *
+ * @param reqModes {@code reqModes} parameter to be passed.
+ * @return {@link AndroidFuture<Integer>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public AndroidFuture<Integer> getCursorCapsMode(int reqModes) {
+ final AndroidFuture<Integer> future = new AndroidFuture<>();
+ try {
+ mConnection.getCursorCapsMode(createHeader(), reqModes, future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ return future;
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#clearMetaKeyStates(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param states {@code states} parameter to be passed.
+ */
+ @AnyThread
+ public void clearMetaKeyStates(int states) {
+ try {
+ mConnection.clearMetaKeyStates(createHeader(), states);
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index f7341a5c4574..10c83c34f417 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -186,7 +186,7 @@ public final class InputBindResult implements Parcelable {
/**
* The accessibility services.
*/
- public SparseArray<IInputMethodSession> accessibilitySessions;
+ public SparseArray<IAccessibilityInputMethodSession> accessibilitySessions;
/**
* The input channel used to send input events to this IME.
@@ -231,8 +231,8 @@ public final class InputBindResult implements Parcelable {
*
* @param result A result code defined in {@link ResultCode}.
* @param method {@link IInputMethodSession} to interact with the IME.
- * @param accessibilitySessions {@link IInputMethodSession} to interact with accessibility
- * services.
+ * @param accessibilitySessions {@link IAccessibilityInputMethodSession} to interact with
+ * accessibility services.
* @param channel {@link InputChannel} to forward input events to the IME.
* @param id The {@link String} representations of the IME, which is the same as
* {@link android.view.inputmethod.InputMethodInfo#getId()} and
@@ -242,7 +242,8 @@ public final class InputBindResult implements Parcelable {
* {@code suppressesSpellChecker="true"}.
*/
public InputBindResult(@ResultCode int result,
- IInputMethodSession method, SparseArray<IInputMethodSession> accessibilitySessions,
+ IInputMethodSession method,
+ SparseArray<IAccessibilityInputMethodSession> accessibilitySessions,
InputChannel channel, String id, int sequence,
@Nullable Matrix virtualDisplayToScreenMatrix,
boolean isInputMethodSuppressingSpellChecker) {
@@ -271,8 +272,9 @@ public final class InputBindResult implements Parcelable {
accessibilitySessions = new SparseArray<>(n);
while (n > 0) {
int key = source.readInt();
- IInputMethodSession value =
- IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+ IAccessibilityInputMethodSession value =
+ IAccessibilityInputMethodSession.Stub.asInterface(
+ source.readStrongBinder());
accessibilitySessions.append(key, value);
n--;
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java b/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java
new file mode 100644
index 000000000000..b2ab819eb8a5
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/RemoteAccessibilityInputConnection.java
@@ -0,0 +1,210 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.DurationMillisLong;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.KeyEvent;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.view.IInputMethod;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A wrapper object for A11y IME.
+ *
+ * <p>This needs to be public to be referenced from {@link android.app.UiAutomation}.</p>
+ */
+public final class RemoteAccessibilityInputConnection {
+ private static final String TAG = "RemoteA11yInputConnection";
+
+ @DurationMillisLong
+ private static final int MAX_WAIT_TIME_MILLIS = 2000;
+
+ @NonNull
+ IRemoteAccessibilityInputConnectionInvoker mInvoker;
+
+ /**
+ * Signaled when the system decided to take away IME focus from the target app.
+ *
+ * <p>This is expected to be signaled immediately when the IME process receives
+ * {@link IInputMethod#unbindInput()}.</p>
+ */
+ @NonNull
+ private final CancellationGroup mCancellationGroup;
+
+ public RemoteAccessibilityInputConnection(
+ @NonNull IRemoteAccessibilityInputConnection connection,
+ @NonNull CancellationGroup cancellationGroup) {
+ mInvoker = IRemoteAccessibilityInputConnectionInvoker.create(connection);
+ mCancellationGroup = cancellationGroup;
+ }
+
+ public RemoteAccessibilityInputConnection(@NonNull RemoteAccessibilityInputConnection original,
+ int sessionId) {
+ mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
+ mCancellationGroup = original.mCancellationGroup;
+ }
+
+ /**
+ * Test if this object holds the given {@link IRemoteAccessibilityInputConnection} or not.
+ *
+ * @param connection {@link IRemoteAccessibilityInputConnection} to be tested.
+ * @return {@code true} if this object holds the same object.
+ */
+ @AnyThread
+ public boolean isSameConnection(@NonNull IRemoteAccessibilityInputConnection connection) {
+ return mInvoker.isSameConnection(connection);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#commitText(InputConnectionCommandHeader,
+ * CharSequence, int, TextAttribute)}.
+ *
+ * @param text The {@code "text"} parameter to be passed.
+ * @param newCursorPosition The {@code "newCursorPosition"} parameter to be passed.
+ * @param textAttribute The {@code "textAttribute"} parameter to be passed.
+ */
+ @AnyThread
+ public void commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ mInvoker.commitText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#setSelection(InputConnectionCommandHeader,
+ * int, int)}.
+ *
+ * @param start The {@code "start"} parameter to be passed.
+ * @param end The {@code "end"} parameter to be passed.
+ */
+ @AnyThread
+ public void setSelection(int start, int end) {
+ mInvoker.setSelection(start, end);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getSurroundingText(
+ * InputConnectionCommandHeader, int, int, int, AndroidFuture)}.
+ *
+ * @param beforeLength The {@code "beforeLength"} parameter to be passed.
+ * @param afterLength The {@code "afterLength"} parameter to be passed.
+ * @param flags The {@code "flags"} parameter to be passed.
+ * @return The {@link SurroundingText} object returned from the target application.
+ */
+ @AnyThread
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ if (beforeLength < 0) {
+ throw new IllegalArgumentException("beforeLength cannot be negative but was "
+ + beforeLength);
+ }
+ if (afterLength < 0) {
+ throw new IllegalArgumentException("afterLength cannot be negative but was "
+ + afterLength);
+ }
+ if (mCancellationGroup.isCanceled()) {
+ return null;
+ }
+
+ final CompletableFuture<SurroundingText> value = mInvoker.getSurroundingText(beforeLength,
+ afterLength, flags);
+ return CompletableFutureUtil.getResultOrNull(
+ value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#deleteSurroundingText(
+ * InputConnectionCommandHeader, int, int)}.
+ *
+ * @param beforeLength The {@code "beforeLength"} parameter to be passed.
+ * @param afterLength The {@code "afterLength"} parameter to be passed.
+ */
+ @AnyThread
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ mInvoker.deleteSurroundingText(beforeLength, afterLength);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#sendKeyEvent(InputConnectionCommandHeader,
+ * KeyEvent)}.
+ *
+ * @param event The {@code "event"} parameter to be passed.
+ */
+ @AnyThread
+ public void sendKeyEvent(KeyEvent event) {
+ mInvoker.sendKeyEvent(event);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performEditorAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param actionCode The {@code "actionCode"} parameter to be passed.
+ */
+ @AnyThread
+ public void performEditorAction(int actionCode) {
+ mInvoker.performEditorAction(actionCode);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#performContextMenuAction(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param id The {@code "id"} parameter to be passed.
+ */
+ @AnyThread
+ public void performContextMenuAction(int id) {
+ mInvoker.performContextMenuAction(id);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#getCursorCapsMode(
+ * InputConnectionCommandHeader, int, AndroidFuture)}.
+ *
+ * @param reqModes The {@code "reqModes"} parameter to be passed.
+ * @return integer result returned from the target application.
+ */
+ @AnyThread
+ public int getCursorCapsMode(int reqModes) {
+ if (mCancellationGroup.isCanceled()) {
+ return 0;
+ }
+
+ final CompletableFuture<Integer> value = mInvoker.getCursorCapsMode(reqModes);
+
+ return CompletableFutureUtil.getResultOrZero(
+ value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+ }
+
+ /**
+ * Invokes {@link IRemoteAccessibilityInputConnection#clearMetaKeyStates(
+ * InputConnectionCommandHeader, int)}.
+ *
+ * @param states The {@code "states"} parameter to be passed.
+ */
+ @AnyThread
+ public void clearMetaKeyStates(int states) {
+ mInvoker.clearMetaKeyStates(states);
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 7f11e3018849..12b522bb98f2 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -66,6 +66,13 @@ import java.util.function.Supplier;
* {@link IInputContext} binder calls in the IME client (editor app) process, and forwards them to
* {@link InputConnection} that the IME client provided, on the {@link Looper} associated to the
* {@link InputConnection}.</p>
+ *
+ * <p>{@link com.android.internal.inputmethod.RemoteAccessibilityInputConnection} code is executed
+ * in the {@link android.accessibilityservice.AccessibilityService} process. It makes
+ * {@link com.android.internal.inputmethod.IRemoteAccessibilityInputConnection} binder calls under
+ * the hood. {@link #mAccessibilityInputConnection} receives the binder calls in the IME client
+ * (editor app) process, and forwards them to {@link InputConnection} that the IME client provided,
+ * on the {@link Looper} associated to the {@link InputConnection}.</p>
*/
public final class RemoteInputConnectionImpl extends IInputContext.Stub {
private static final String TAG = "RemoteInputConnectionImpl";
@@ -1043,6 +1050,179 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
+ private final IRemoteAccessibilityInputConnection mAccessibilityInputConnection =
+ new IRemoteAccessibilityInputConnection.Stub() {
+ @Dispatching(cancellable = true)
+ @Override
+ public void commitText(InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("commitTextFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitText on inactive InputConnection");
+ return;
+ }
+ // A11yIME's commitText() also triggers finishComposingText() automatically.
+ ic.beginBatchEdit();
+ ic.finishComposingText();
+ ic.commitText(text, newCursorPosition, textAttribute);
+ ic.endBatchEdit();
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void setSelection(InputConnectionCommandHeader header, int start, int end) {
+ dispatchWithTracing("setSelectionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setSelection on inactive InputConnection");
+ return;
+ }
+ ic.setSelection(start, end);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+ int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
+ dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return null; // cancelled
+ }
+ final InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSurroundingText on inactive InputConnection");
+ return null;
+ }
+ if (beforeLength < 0) {
+ Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+ + " beforeLength=" + beforeLength);
+ return null;
+ }
+ if (afterLength < 0) {
+ Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+ + " afterLength=" + afterLength);
+ return null;
+ }
+ return ic.getSurroundingText(beforeLength, afterLength, flags);
+ }, useImeTracing() ? result -> buildGetSurroundingTextProto(
+ beforeLength, afterLength, flags, result) : null);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+ int afterLength) {
+ dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
+ return;
+ }
+ ic.deleteSurroundingText(beforeLength, afterLength);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
+ dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "sendKeyEvent on inactive InputConnection");
+ return;
+ }
+ ic.sendKeyEvent(event);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performEditorAction(InputConnectionCommandHeader header, int id) {
+ dispatchWithTracing("performEditorActionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performEditorAction on inactive InputConnection");
+ return;
+ }
+ ic.performEditorAction(id);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
+ dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performContextMenuAction on inactive InputConnection");
+ return;
+ }
+ ic.performContextMenuAction(id);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
+ AndroidFuture future /* T=Integer */) {
+ dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return 0; // cancelled
+ }
+ final InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
+ return 0;
+ }
+ return ic.getCursorCapsMode(reqModes);
+ }, useImeTracing() ? result -> buildGetCursorCapsModeProto(reqModes, result) : null);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
+ dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
+ return;
+ }
+ ic.clearMetaKeyStates(states);
+ });
+ }
+ };
+
+ /**
+ * @return {@link IRemoteAccessibilityInputConnection} associated with this object.
+ */
+ public IRemoteAccessibilityInputConnection asIRemoteAccessibilityInputConnection() {
+ return mAccessibilityInputConnection;
+ }
+
private void dispatch(@NonNull Runnable runnable) {
// If we are calling this from the target thread, then we can call right through.
// Otherwise, we need to send the message to the target thread.
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 3da37f89f83e..6424989c6b4f 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -61,6 +61,9 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW;
@@ -184,6 +187,9 @@ public class InteractionJankMonitor {
public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46;
public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47;
public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48;
+ public static final int CUJ_SPLIT_SCREEN_ENTER = 49;
+ public static final int CUJ_SPLIT_SCREEN_EXIT = 50;
+ public static final int CUJ_SPLIT_SCREEN_RESIZE = 51;
private static final int NO_STATSD_LOGGING = -1;
@@ -241,6 +247,9 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE,
};
private static volatile InteractionJankMonitor sInstance;
@@ -309,7 +318,10 @@ public class InteractionJankMonitor {
CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS,
CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS,
CUJ_SUW_LOADING_TO_NEXT_FLOW,
- CUJ_SUW_LOADING_SCREEN_FOR_STATUS
+ CUJ_SUW_LOADING_SCREEN_FOR_STATUS,
+ CUJ_SPLIT_SCREEN_ENTER,
+ CUJ_SPLIT_SCREEN_EXIT,
+ CUJ_SPLIT_SCREEN_RESIZE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -726,6 +738,12 @@ public class InteractionJankMonitor {
return "SUW_LOADING_TO_NEXT_FLOW";
case CUJ_SUW_LOADING_SCREEN_FOR_STATUS:
return "SUW_LOADING_SCREEN_FOR_STATUS";
+ case CUJ_SPLIT_SCREEN_ENTER:
+ return "SPLIT_SCREEN_ENTER";
+ case CUJ_SPLIT_SCREEN_EXIT:
+ return "SPLIT_SCREEN_EXIT";
+ case CUJ_SPLIT_SCREEN_RESIZE:
+ return "CUJ_SPLIT_SCREEN_RESIZE";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 616411fee5f2..d7bb2cb10b8c 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -22,6 +22,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -54,7 +55,8 @@ interface IInputMethodManager {
in IInputMethodClient client, in IBinder windowToken,
/* @StartInputFlags */ int startInputFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
- int windowFlags, in EditorInfo attribute, IInputContext inputContext,
+ int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
+ in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion);
void showInputMethodPickerFromClient(in IInputMethodClient client,
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index c9953513a97a..b866723954b5 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -198,6 +198,11 @@ public class LocalImageResolver {
}
final Size size = info.getSize();
+ if (size.getWidth() <= maxWidth && size.getHeight() <= maxHeight) {
+ // We don't want to upscale images needlessly.
+ return;
+ }
+
if (size.getWidth() > size.getHeight()) {
if (size.getWidth() > maxWidth) {
final int targetHeight = size.getHeight() * maxWidth / size.getWidth();
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7bc69055292d..21bbac0b0a7d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,6 +110,8 @@ using android::base::GetBoolProperty;
using android::zygote::ZygoteFailure;
+using Action = android_mallopt_gwp_asan_options_t::Action;
+
// This type is duplicated in fd_utils.h
typedef const std::function<void(std::string)>& fail_fn_t;
@@ -1717,16 +1719,24 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// runtime.
runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT_ENABLED;
- bool forceEnableGwpAsan = false;
+ const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+ android_mallopt_gwp_asan_options_t gwp_asan_options;
+ // The system server doesn't have its nice name set by the time SpecializeCommon is called.
+ gwp_asan_options.program_name = nice_name_ptr ?: process_name;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
default:
case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+ gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
break;
case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
- forceEnableGwpAsan = true;
- [[fallthrough]];
+ gwp_asan_options.desire = Action::TURN_ON_FOR_APP;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
- android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+ gwp_asan_options.desire = Action::TURN_ON_WITH_SAMPLING;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
}
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
// runtime.
@@ -1739,7 +1749,6 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
AStatsSocket_close();
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
- const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 679a4f070290..add645dee718 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -296,7 +296,7 @@ jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, j
++buffersAllocd;
// MMap explicitly to get it page aligned.
void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
// Currently we mmap and unmap one for every request handled by the Java code.
// That could be improved, but unclear it matters.
if (bufferMem == MAP_FAILED) {
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index c5eaf42784ab..df5e0a942ca7 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -110,6 +110,7 @@ message UsbHostManagerProto {
repeated UsbDeviceProto devices = 2;
optional int32 num_connects = 3;
repeated UsbConnectionRecordProto connections = 4;
+ repeated UsbDirectMidiDeviceProto midi_devices = 5;
}
message UsbDeviceProto {
@@ -305,6 +306,42 @@ message UsbMidiDeviceProto {
optional string device_address = 3;
}
+message UsbDirectMidiDeviceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 num_inputs = 1;
+ optional int32 num_outputs = 2;
+ optional bool is_universal = 3;
+ optional string name = 4;
+ optional UsbMidiBlockParserProto block_parser = 5;
+}
+
+message UsbMidiBlockParserProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 length = 1;
+ optional int32 descriptor_type = 2;
+ optional int32 descriptor_subtype = 3;
+ optional int32 total_length = 4;
+ repeated UsbGroupTerminalBlockProto block = 5;
+}
+
+message UsbGroupTerminalBlockProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 length = 1;
+ optional int32 descriptor_type = 2;
+ optional int32 descriptor_subtype = 3;
+ optional int32 group_block_id = 4;
+ optional int32 group_terminal_block_type = 5;
+ optional int32 group_terminal = 6;
+ optional int32 num_group_terminals = 7;
+ optional int32 block_item = 8;
+ optional int32 midi_protocol = 9;
+ optional int32 max_input_bandwidth = 10;
+ optional int32 max_output_bandwidth = 11;
+}
+
message UsbSettingsManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/res/drawable/chooser_pinned_background.xml b/core/res/res/drawable/chooser_pinned_background.xml
index fbbe8c107fb9..e8c9910e4e4b 100644
--- a/core/res/res/drawable/chooser_pinned_background.xml
+++ b/core/res/res/drawable/chooser_pinned_background.xml
@@ -17,7 +17,8 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_chooser_pin"
- android:gravity="start|center_vertical"
+ android:gravity="start|top"
+ android:top="4dp"
android:width="12dp"
android:height="12dp"
android:start="0dp"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 757f40963bf7..b23d8cab74d4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1864,6 +1864,11 @@
-->
<string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
+ <!-- The package name of the default search selector app. Must be granted the POST_NOTIFICATIONS
+ permission.
+ -->
+ <string name="config_defaultSearchSelectorPackageName" translatable="false"></string>
+
<!-- Whether to enable geocoder overlay which allows geocoder to be replaced
by an app at run-time. When disabled, only the
config_geocoderProviderPackageName package will be searched for
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ff87ac07b291..824dd8be0160 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -876,19 +876,19 @@
<string name="permgroupdesc_sms">send and view SMS messages</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgrouplab_storage">Files &amp; documents</string>
+ <string name="permgrouplab_storage">Files and documents</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_storage">access files and documents on your device</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
- <string name="permgrouplab_readMediaAural">Music &amp; other audio</string>
+ <string name="permgrouplab_readMediaAural">Music and audio</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
- <string name="permgroupdesc_readMediaAural">access audio files on your device</string>
+ <string name="permgroupdesc_readMediaAural">access music and audio on your device</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
- <string name="permgrouplab_readMediaVisual">Photos &amp; videos</string>
+ <string name="permgrouplab_readMediaVisual">Photos and videos</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
- <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string>
+ <string name="permgroupdesc_readMediaVisual">access photos and videos on your device</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_microphone">Microphone</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 333482272789..21b235104694 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3413,6 +3413,9 @@
<!-- Network Recommendation -->
<java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
+ <!-- Search Selector -->
+ <java-symbol type="string" name="config_defaultSearchSelectorPackageName" />
+
<!-- Optional IPsec algorithms -->
<java-symbol type="array" name="config_optionalIpSecAlgorithms" />
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index d5223492a83c..b331a24fedc6 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,6 +15,9 @@
*/
package android.content.pm;
+import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_OTHER_CAPABILITY;
+import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
+import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_SELF_CAPABILITY;
import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
@@ -45,6 +48,7 @@ import java.util.Set;
public class SigningDetailsTest {
private static final int DEFAULT_CAPABILITIES =
INSTALLED_DATA | SHARED_USER_ID | PERMISSION | AUTH;
+ private static final int CURRENT_SIGNER_CAPABILITIES = DEFAULT_CAPABILITIES | ROLLBACK;
// Some of the tests in this class require valid certificate encodings from which to pull the
// public key for the SigningDetails; the following are all DER encoded EC X.509 certificates.
@@ -368,10 +372,10 @@ public class SigningDetailsTest {
}
@Test
- public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithModifiedCaps()
+ public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithProvidedCaps()
throws Exception {
// This test verifies when two lineages consist of the same signers but have different
- // capabilities the more restrictive capabilities are returned.
+ // capabilities, the capabilities of the provided lineage are returned.
SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
SECOND_SIGNATURE, THIRD_SIGNATURE);
SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
@@ -384,68 +388,135 @@ public class SigningDetailsTest {
defaultCapabilitiesDetails);
assertEquals(modifiedCapabilitiesDetails, result1);
- assertTrue(result2 == modifiedCapabilitiesDetails);
+ assertEquals(defaultCapabilitiesDetails, result2);
+ }
+
+ @Test
+ public void
+ mergeLineageWith_sameLineageDifferentCapsRestrictedRule_returnsLineageWithModifiedCaps()
+ throws Exception {
+ // This test verifies when two lineages consist of the same signers but have different
+ // capabilities, and the restricted merge rule is used, the more restrictive capabilities
+ // are returned.
+ SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, THIRD_SIGNATURE);
+ SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{INSTALLED_DATA, INSTALLED_DATA, INSTALLED_DATA});
+
+ SigningDetails result1 = defaultCapabilitiesDetails.mergeLineageWith(
+ modifiedCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY);
+ SigningDetails result2 = modifiedCapabilitiesDetails.mergeLineageWith(
+ defaultCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY);
+
+ assertEquals(modifiedCapabilitiesDetails, result1);
+ assertEquals(modifiedCapabilitiesDetails, result2);
}
@Test
public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps()
throws Exception {
- // This test verifies the following scenario:
- // - First lineage has signers A -> B with modified capabilities for A and B
- // - Second lineage has signers B -> C with modified capabilities for B and C
- // The merged lineage should be A -> B -> C with the most restrictive capabilities for B
- // since it is in both lineages.
+ // This test verifies the merge of two lineages with overlapping signers and modified caps
+ // returns the full lineage with expected capabilities based on the provided merge rule.
int[] firstCapabilities =
new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION};
int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH,
INSTALLED_DATA | SHARED_USER_ID | AUTH};
- int[] expectedCapabilities =
+ int[] expectedRestrictedCapabilities =
new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0],
secondCapabilities[1]};
+ int[] expectedCapabilities1 =
+ new int[]{firstCapabilities[0], secondCapabilities[0], secondCapabilities[1]};
+ int[] expectedCapabilities2 =
+ new int[]{firstCapabilities[0], firstCapabilities[1], secondCapabilities[1]};
SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities);
- SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
+ SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
- expectedCapabilities);
-
- SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails);
- SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails);
-
- assertEquals(expectedDetails, result1);
- assertEquals(expectedDetails, result2);
+ expectedRestrictedCapabilities);
+ SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ expectedCapabilities1);
+ SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ expectedCapabilities2);
+
+ SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails,
+ MERGE_OTHER_CAPABILITY);
+ SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails,
+ MERGE_SELF_CAPABILITY);
+ SigningDetails result3 = firstDetails.mergeLineageWith(secondDetails,
+ MERGE_SELF_CAPABILITY);
+ SigningDetails result4 = secondDetails.mergeLineageWith(firstDetails,
+ MERGE_OTHER_CAPABILITY);
+ SigningDetails result5 = firstDetails.mergeLineageWith(secondDetails,
+ MERGE_RESTRICTED_CAPABILITY);
+ SigningDetails result6 = secondDetails.mergeLineageWith(firstDetails,
+ MERGE_RESTRICTED_CAPABILITY);
+
+ assertEquals(expectedDetails1, result1);
+ assertEquals(expectedDetails1, result2);
+ assertEquals(expectedDetails2, result3);
+ assertEquals(expectedDetails2, result4);
+ assertEquals(expectedRestrictedDetails, result5);
+ assertEquals(expectedRestrictedDetails, result6);
}
@Test
public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps()
throws Exception {
- // This test verifies the following scenario:
- // - First lineage has signers B -> C with modified capabilities
- // - Second lineage has signers A -> B -> C -> D with modified capabilities
- // The merged lineage should be A -> B -> C -> D with the most restrictive capabilities for
- // B and C since they are in both lineages.
+ // This test verifies the merge of a full lineage and a subset of that lineage with
+ // modified caps returns the full lineage with expected capabilities based on the
+ // provided merge rule.
int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION,
DEFAULT_CAPABILITIES | ROLLBACK};
int[] fullCapabilities =
new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES};
- int[] expectedCapabilities =
+ int[] expectedRestrictedCapabilities =
new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1],
subCapabilities[1] & fullCapabilities[2], fullCapabilities[3]};
+ int[] expectedCapabilities1 =
+ new int[]{fullCapabilities[0], fullCapabilities[1], fullCapabilities[2],
+ fullCapabilities[3]};
+ int[] expectedCapabilities2 =
+ new int[]{fullCapabilities[0], subCapabilities[0], subCapabilities[1],
+ fullCapabilities[3]};
SigningDetails subLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities);
SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
fullCapabilities);
- SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities(
+ SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities(
new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
- expectedCapabilities);
-
- SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails);
- SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails);
-
- assertEquals(expectedDetails, result1);
- assertEquals(expectedDetails, result2);
+ expectedRestrictedCapabilities);
+ SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
+ expectedCapabilities1);
+ SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE},
+ expectedCapabilities2);
+
+ SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails,
+ MERGE_OTHER_CAPABILITY);
+ SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails,
+ MERGE_SELF_CAPABILITY);
+ SigningDetails result3 = subLineageDetails.mergeLineageWith(fullLineageDetails,
+ MERGE_SELF_CAPABILITY);
+ SigningDetails result4 = fullLineageDetails.mergeLineageWith(subLineageDetails,
+ MERGE_OTHER_CAPABILITY);
+ SigningDetails result5 = subLineageDetails.mergeLineageWith(fullLineageDetails,
+ MERGE_RESTRICTED_CAPABILITY);
+ SigningDetails result6 = fullLineageDetails.mergeLineageWith(subLineageDetails,
+ MERGE_RESTRICTED_CAPABILITY);
+
+ assertEquals(expectedDetails1, result1);
+ assertEquals(expectedDetails1, result2);
+ assertEquals(expectedDetails2, result3);
+ assertEquals(expectedDetails2, result4);
+ assertEquals(expectedRestrictedDetails, result5);
+ assertEquals(expectedRestrictedDetails, result6);
}
@Test
@@ -466,6 +537,39 @@ public class SigningDetailsTest {
}
@Test
+ public void mergeLineageWith_modifiedCaps_returnsCapsFromProvidedLineage()
+ throws Exception {
+ // By default, when merging two lineage instances, the initial instance should represent a
+ // shared lineage while the provided lineage represents that of a newly installed / updated
+ // package. The shared lineage should contain any previous capability modifications from
+ // the default while the provided lineage has an opportunity to modify what was previously
+ // set. Initially, the most restrictive capabilities were always retained by the returned
+ // lineage, so apps had no mechanism to roll back a restriction to a previous signer. To
+ // allow this, a merge rule can be specified to indicate how differences in capabilities
+ // in common signers should be handled with the default using the capabilities from the
+ // provided lineage.
+ int[] firstCapabilities = new int[]{INSTALLED_DATA | PERMISSION | AUTH,
+ CURRENT_SIGNER_CAPABILITIES};
+ int[] secondCapabilities =
+ new int[]{DEFAULT_CAPABILITIES, CURRENT_SIGNER_CAPABILITIES};
+ SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
+ SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities);
+ // By default, the resulting capabilities should be that of the provided lineage.
+ SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities);
+ SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities);
+
+ SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails);
+ SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails);
+
+ assertEquals(expectedDetails1, result1);
+ assertEquals(expectedDetails2, result2);
+ }
+
+ @Test
public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception {
// If neither SigningDetails have a lineage but they have the same single signer then
// hasCommonAncestor should return true.
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 4319b9703730..faeae2cb6698 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -609,7 +609,8 @@ public class DisplayCutoutTest {
private static DisplayCutout.CutoutPathParserInfo createParserInfo(
@Surface.Rotation int rotation) {
return new DisplayCutout.CutoutPathParserInfo(
- 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
- rotation, 0f /* scale */);
+ 0 /* displayWidth */, 0 /* displayHeight */, 0 /* displayWidth */,
+ 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
+ rotation, 0f /* scale */, 0f /* displaySizeRatio */);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index e7a23f262753..43fba529182e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -516,8 +516,6 @@ public class ResolverActivityTest {
onView(withText(R.string.resolver_work_tab))
.perform(click());
waitForIdle();
- // wait for the share sheet to expand
- Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
onView(first(allOf(withText(workResolvedComponentInfos.get(0)
.getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
.perform(click());
@@ -616,8 +614,6 @@ public class ResolverActivityTest {
onView(withText(R.string.resolver_work_tab))
.perform(click());
waitForIdle();
- // wait for the share sheet to expand
- Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
onView(first(allOf(
withText(workResolvedComponentInfos.get(0)
.getResolveInfoAt(0).activityInfo.applicationInfo.name),
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
index 033c3cae5beb..c63d18bfa531 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
@@ -195,6 +195,17 @@ public class LocalImageResolverTest {
}
@Test
+ public void resolveImage_smallBitmapIcon_passedSmallerSize_dontResize() {
+ Icon icon = Icon.createWithResource(mContext.getResources(), R.drawable.test32x24);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext, 600, 450);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ assertThat(bd.getBitmap().getWidth()).isEqualTo(32);
+ assertThat(bd.getBitmap().getHeight()).isEqualTo(24);
+ }
+
+ @Test
public void resolveImage_largeBitmapIcon_passedSize_resizeToDefinedSize() {
Icon icon = Icon.createWithBitmap(
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
diff --git a/graphics/TEST_MAPPING b/graphics/TEST_MAPPING
index 10bd0ee906fd..abeaf1996ca7 100644
--- a/graphics/TEST_MAPPING
+++ b/graphics/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "CtsGraphicsTestCases"
+ "name": "CtsGraphicsTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
index 2e54e63a5b7a..698133287f63 100644
--- a/keystore/java/android/security/GenerateRkpKey.java
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -16,6 +16,8 @@
package android.security;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -24,6 +26,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -57,6 +61,21 @@ public class GenerateRkpKey {
private Context mContext;
private CountDownLatch mCountDownLatch;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ IGenerateRkpKeyService.Status.OK,
+ IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY,
+ IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR,
+ IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED,
+ IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR,
+ IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR,
+ IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR,
+ IGenerateRkpKeyService.Status.INTERNAL_ERROR,
+ })
+ public @interface Status {
+ }
+
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
@@ -81,12 +100,14 @@ public class GenerateRkpKey {
mContext = context;
}
- private void bindAndSendCommand(int command, int securityLevel) throws RemoteException {
+ @Status
+ private int bindAndSendCommand(int command, int securityLevel) throws RemoteException {
Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ int returnCode = IGenerateRkpKeyService.Status.OK;
if (comp == null) {
// On a system that does not use RKP, the RemoteProvisioner app won't be installed.
- return;
+ return returnCode;
}
intent.setComponent(comp);
mCountDownLatch = new CountDownLatch(1);
@@ -102,7 +123,7 @@ public class GenerateRkpKey {
if (mBinder != null) {
switch (command) {
case NOTIFY_EMPTY:
- mBinder.generateKey(securityLevel);
+ returnCode = mBinder.generateKey(securityLevel);
break;
case NOTIFY_KEY_GENERATED:
mBinder.notifyKeyGenerated(securityLevel);
@@ -112,16 +133,21 @@ public class GenerateRkpKey {
}
} else {
Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService.");
+ returnCode = IGenerateRkpKeyService.Status.INTERNAL_ERROR;
}
mContext.unbindService(mConnection);
+ return returnCode;
}
/**
* Fulfills the use case of (2) described in the class documentation. Blocks until the
* RemoteProvisioner application can get new attestation keys signed by the server.
+ * @return the status of the key generation
*/
- public void notifyEmpty(int securityLevel) throws RemoteException {
- bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
+ @CheckResult
+ @Status
+ public int notifyEmpty(int securityLevel) throws RemoteException {
+ return bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
}
/**
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
index 5f1d6693c23a..eeaeb27a7c77 100644
--- a/keystore/java/android/security/IGenerateRkpKeyService.aidl
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -26,11 +26,35 @@ package android.security;
* @hide
*/
interface IGenerateRkpKeyService {
+ @JavaDerive(toString=true)
+ @Backing(type="int")
+ enum Status {
+ /** No error(s) occurred */
+ OK = 0,
+ /** Unable to provision keys due to a lack of internet connectivity. */
+ NO_NETWORK_CONNECTIVITY = 1,
+ /** An error occurred while communicating with the RKP server. */
+ NETWORK_COMMUNICATION_ERROR = 2,
+ /** The given device was not registered with the RKP backend. */
+ DEVICE_NOT_REGISTERED = 4,
+ /** The RKP server returned an HTTP client error, indicating a misbehaving client. */
+ HTTP_CLIENT_ERROR = 5,
+ /** The RKP server returned an HTTP server error, indicating something went wrong on the server. */
+ HTTP_SERVER_ERROR = 6,
+ /** The RKP server returned an HTTP status that is unknown. This should never happen. */
+ HTTP_UNKNOWN_ERROR = 7,
+ /** An unexpected internal error occurred. This should never happen. */
+ INTERNAL_ERROR = 8,
+ }
+
/**
* Ping the provisioner service to let it know an app generated a key. This may or may not have
* consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
*/
oneway void notifyKeyGenerated(in int securityLevel);
- /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
- void generateKey(in int securityLevel);
+
+ /**
+ * Ping the provisioner service to indicate there are no remaining attestation keys left.
+ */
+ Status generateKey(in int securityLevel);
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 3d53cfb388e1..c2cd6ffc622c 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -345,6 +345,12 @@ public class KeyStore2 {
case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
return new KeyStoreException(errorCode, "Key permanently invalidated",
serviceErrorMessage);
+ case ResponseCode.OUT_OF_KEYS:
+ // Getting a more specific RKP status requires the security level, which we
+ // don't have here. Higher layers of the stack can interpret this exception
+ // and add more flavor.
+ return new KeyStoreException(errorCode, serviceErrorMessage,
+ KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
default:
return new KeyStoreException(errorCode, String.valueOf(errorCode),
serviceErrorMessage);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 5950b5bc7231..40659f5dbfb0 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -28,6 +28,7 @@ import android.hardware.security.keymint.Tag;
import android.os.Build;
import android.os.RemoteException;
import android.security.GenerateRkpKey;
+import android.security.IGenerateRkpKeyService;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -624,7 +625,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
* GenerateRkpKey.notifyEmpty() will delay for a while before returning.
*/
result = generateKeyPairHelper();
- if (result.rkpStatus == KeyStoreException.RKP_SUCCESS) {
+ if (result.rkpStatus == KeyStoreException.RKP_SUCCESS && result.keyPair != null) {
return result.keyPair;
}
}
@@ -706,27 +707,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
success = true;
KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey());
return new GenerateKeyPairHelperResult(0, kp);
- } catch (android.security.KeyStoreException e) {
+ } catch (KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
case ResponseCode.OUT_OF_KEYS:
- GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
- .currentApplication());
- try {
- //TODO: When detailed error information is available from the remote
- //provisioner, propagate it up.
- keyGen.notifyEmpty(securityLevel);
- } catch (RemoteException f) {
- KeyStoreException ksException = new KeyStoreException(
- ResponseCode.OUT_OF_KEYS,
- "Remote exception: " + f.getMessage(),
- KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
- throw new ProviderException("Failed to talk to RemoteProvisioner",
- ksException);
- }
- return new GenerateKeyPairHelperResult(
- KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE, null);
+ throw makeOutOfKeysException(e, securityLevel);
default:
ProviderException p = new ProviderException("Failed to generate key pair.", e);
if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
@@ -752,6 +738,52 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
}
+ // In case keystore reports OUT_OF_KEYS, call this handler in an attempt to remotely provision
+ // some keys.
+ private ProviderException makeOutOfKeysException(KeyStoreException e, int securityLevel) {
+ GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
+ .currentApplication());
+ KeyStoreException ksException;
+ try {
+ final int keyGenStatus = keyGen.notifyEmpty(securityLevel);
+ // Default stance: temporary error. This is a hint to the caller to try again with
+ // exponential back-off.
+ int rkpStatus;
+ switch (keyGenStatus) {
+ case IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY:
+ rkpStatus = KeyStoreException.RKP_FETCHING_PENDING_CONNECTIVITY;
+ break;
+ case IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED:
+ rkpStatus = KeyStoreException.RKP_SERVER_REFUSED_ISSUANCE;
+ break;
+ case IGenerateRkpKeyService.Status.OK:
+ // This will actually retry once immediately, so on "OK" go ahead and return
+ // "temporarily unavailable". @see generateKeyPair
+ case IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR:
+ case IGenerateRkpKeyService.Status.INTERNAL_ERROR:
+ default:
+ // These errors really should never happen. The best we can do is assume they
+ // are transient and hint to the caller to retry with back-off.
+ rkpStatus = KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE;
+ break;
+ }
+ ksException = new KeyStoreException(
+ ResponseCode.OUT_OF_KEYS,
+ "Out of RKP keys due to IGenerateRkpKeyService status: " + keyGenStatus,
+ rkpStatus);
+ } catch (RemoteException f) {
+ ksException = new KeyStoreException(
+ ResponseCode.OUT_OF_KEYS,
+ "Remote exception: " + f.getMessage(),
+ KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
+ }
+ ksException.initCause(e);
+ return new ProviderException("Failed to talk to RemoteProvisioner", ksException);
+ }
+
private void addAttestationParameters(@NonNull List<KeyParameter> params)
throws ProviderException, IllegalArgumentException, DeviceIdAttestationException {
byte[] challenge = mSpec.getAttestationChallenge();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index e20cef2bec4e..ca420c64e961 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -40,6 +40,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.ArraySet;
+import android.util.Log;
import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -59,6 +60,7 @@ import java.util.function.Consumer;
*/
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent {
+ private static final String TAG = "SplitController";
@VisibleForTesting
final SplitPresenter mPresenter;
@@ -229,8 +231,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
if (taskContainer.isEmpty()) {
// Cleanup the TaskContainer if it becomes empty.
- mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
- mTaskContainers.remove(taskContainer.mTaskId);
+ mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
+ mTaskContainers.remove(taskContainer.getTaskId());
}
return;
}
@@ -241,13 +243,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskContainer == null) {
return;
}
- final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration);
+ final boolean wasInPip = isInPictureInPicture(taskContainer.getConfiguration());
final boolean isInPIp = isInPictureInPicture(config);
- taskContainer.mConfiguration = config;
+ taskContainer.setConfiguration(config);
// We need to check the animation override when enter/exit PIP or has bounds changed.
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
- if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds())
+ if (taskContainer.setTaskBounds(config.windowConfiguration.getBounds())
&& !isInPIp) {
// We don't care the bounds change when it has already entered PIP.
shouldUpdateAnimationOverride = true;
@@ -257,16 +259,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
- /** Returns {@code true} if the bounds is changed. */
- private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer,
- @NonNull Rect taskBounds) {
- if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) {
- taskContainer.mTaskBounds.set(taskBounds);
- return true;
- }
- return false;
- }
-
/**
* Updates if we should override transition animation. We only want to override if the Task
* bounds is large enough for at least one split rule.
@@ -279,15 +271,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// We only want to override if it supports split.
if (supportSplit(taskContainer)) {
- mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId);
+ mPresenter.startOverrideSplitAnimation(taskContainer.getTaskId());
} else {
- mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
+ mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
}
}
private boolean supportSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
- if (isInPictureInPicture(taskContainer.mConfiguration)) {
+ if (isInPictureInPicture(taskContainer.getConfiguration())) {
return false;
}
// Check if the parent container bounds can support any split rule.
@@ -295,7 +287,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (!(rule instanceof SplitRule)) {
continue;
}
- if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
+ if (mPresenter.shouldShowSideBySide(taskContainer.getTaskBounds(), (SplitRule) rule)) {
return true;
}
}
@@ -425,21 +417,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
+ TaskFragmentContainer newContainer(@NonNull Activity activity, int taskId) {
+ return newContainer(activity, activity, taskId);
+ }
+
/**
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
+ *
+ * @param activity the activity that will be reparented to the TaskFragment.
+ * @param activityInTask activity in the same Task so that we can get the Task bounds if
+ * needed.
+ * @param taskId parent Task of the new TaskFragment.
*/
- TaskFragmentContainer newContainer(@Nullable Activity activity, int taskId) {
+ TaskFragmentContainer newContainer(@Nullable Activity activity,
+ @NonNull Activity activityInTask, int taskId) {
+ if (activityInTask == null) {
+ throw new IllegalArgumentException("activityInTask must not be null,");
+ }
final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskId);
if (!mTaskContainers.contains(taskId)) {
mTaskContainers.put(taskId, new TaskContainer(taskId));
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
taskContainer.mContainers.add(container);
- if (activity != null && !taskContainer.isTaskBoundsInitialized()
- && onTaskBoundsMayChange(taskContainer,
- SplitPresenter.getTaskBoundsFromActivity(activity))) {
- // Initial check before any TaskFragment has appeared.
+ if (!taskContainer.isTaskBoundsInitialized()) {
+ // Get the initial bounds before the TaskFragment has appeared.
+ final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);
+ if (!taskContainer.setTaskBounds(taskBounds)) {
+ Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
+ }
updateAnimationOverride(taskContainer);
}
return container;
@@ -887,6 +894,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
+ @Nullable
+ TaskContainer getTaskContainer(int taskId) {
+ return mTaskContainers.get(taskId);
+ }
+
/**
* Returns {@code true} if an Activity with the provided component name should always be
* expanded to occupy full task bounds. Such activity must not be put in a split.
@@ -1211,37 +1223,4 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return configuration != null
&& configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
}
-
- /** Represents TaskFragments and split pairs below a Task. */
- @VisibleForTesting
- static class TaskContainer {
- /** The unique task id. */
- final int mTaskId;
- /** Active TaskFragments in this Task. */
- final List<TaskFragmentContainer> mContainers = new ArrayList<>();
- /** Active split pairs in this Task. */
- final List<SplitContainer> mSplitContainers = new ArrayList<>();
- /**
- * TaskFragments that the organizer has requested to be closed. They should be removed when
- * the organizer receives {@link #onTaskFragmentVanished(TaskFragmentInfo)} event for them.
- */
- final Set<IBinder> mFinishedContainer = new ArraySet<>();
- /** Available window bounds of this Task. */
- final Rect mTaskBounds = new Rect();
- /** Configuration of the Task. */
- @Nullable
- Configuration mConfiguration;
-
- TaskContainer(int taskId) {
- mTaskId = taskId;
- }
-
- boolean isEmpty() {
- return mContainers.isEmpty() && mFinishedContainer.isEmpty();
- }
-
- boolean isTaskBoundsInitialized() {
- return !mTaskBounds.isEmpty();
- }
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 1b49585ed7dc..716a087203d3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -19,9 +19,9 @@ package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.Activity;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -111,8 +111,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- final TaskFragmentContainer secondaryContainer = mController.newContainer(null,
- primaryContainer.getTaskId());
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(
+ null /* activity */, primaryActivity, primaryContainer.getTaskId());
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
@@ -168,8 +168,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* Creates a new expanded container.
*/
TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
- final TaskFragmentContainer newContainer = mController.newContainer(null,
- launchingActivity.getTaskId());
+ final TaskFragmentContainer newContainer = mController.newContainer(null /* activity */,
+ launchingActivity, launchingActivity.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
createTaskFragment(wct, newContainer.getTaskFragmentToken(),
@@ -236,8 +236,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
launchingActivity.getTaskId());
}
- TaskFragmentContainer secondaryContainer = mController.newContainer(null,
- primaryContainer.getTaskId());
+ TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
+ launchingActivity, primaryContainer.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
@@ -398,20 +398,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
- final Configuration parentConfig = mFragmentParentConfigs.get(
- container.getTaskFragmentToken());
- if (parentConfig != null) {
- return parentConfig.windowConfiguration.getBounds();
+ final int taskId = container.getTaskId();
+ final TaskContainer taskContainer = mController.getTaskContainer(taskId);
+ if (taskContainer == null) {
+ throw new IllegalStateException("Can't find TaskContainer taskId=" + taskId);
}
-
- // If there is no parent yet - then assuming that activities are running in full task bounds
- final Activity topActivity = container.getTopNonFinishingActivity();
- final Rect bounds = topActivity != null ? getParentContainerBounds(topActivity) : null;
-
- if (bounds == null) {
- throw new IllegalStateException("Unknown parent bounds");
- }
- return bounds;
+ return taskContainer.getTaskBounds();
}
@NonNull
@@ -419,22 +411,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final TaskFragmentContainer container = mController.getContainerWithActivity(
activity.getActivityToken());
if (container != null) {
- final Configuration parentConfig = mFragmentParentConfigs.get(
- container.getTaskFragmentToken());
- if (parentConfig != null) {
- return parentConfig.windowConfiguration.getBounds();
- }
+ return getParentContainerBounds(container);
}
-
return getTaskBoundsFromActivity(activity);
}
@NonNull
static Rect getTaskBoundsFromActivity(@NonNull Activity activity) {
+ final WindowConfiguration windowConfiguration =
+ activity.getResources().getConfiguration().windowConfiguration;
if (!activity.isInMultiWindowMode()) {
// In fullscreen mode the max bounds should correspond to the task bounds.
- return activity.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+ return windowConfiguration.getMaxBounds();
}
- return activity.getResources().getConfiguration().windowConfiguration.getBounds();
+ return windowConfiguration.getBounds();
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
new file mode 100644
index 000000000000..be793018d969
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -0,0 +1,97 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.window.TaskFragmentInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/** Represents TaskFragments and split pairs below a Task. */
+class TaskContainer {
+
+ /** The unique task id. */
+ private final int mTaskId;
+
+ /** Available window bounds of this Task. */
+ private final Rect mTaskBounds = new Rect();
+
+ /** Configuration of the Task. */
+ @Nullable
+ private Configuration mConfiguration;
+
+ /** Active TaskFragments in this Task. */
+ final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+
+ /** Active split pairs in this Task. */
+ final List<SplitContainer> mSplitContainers = new ArrayList<>();
+
+ /**
+ * TaskFragments that the organizer has requested to be closed. They should be removed when
+ * the organizer receives {@link SplitController#onTaskFragmentVanished(TaskFragmentInfo)} event
+ * for them.
+ */
+ final Set<IBinder> mFinishedContainer = new ArraySet<>();
+
+ TaskContainer(int taskId) {
+ mTaskId = taskId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ @NonNull
+ Rect getTaskBounds() {
+ return mTaskBounds;
+ }
+
+ /** Returns {@code true} if the bounds is changed. */
+ boolean setTaskBounds(@NonNull Rect taskBounds) {
+ if (!taskBounds.isEmpty() && !mTaskBounds.equals(taskBounds)) {
+ mTaskBounds.set(taskBounds);
+ return true;
+ }
+ return false;
+ }
+
+ /** Whether the Task bounds has been initialized. */
+ boolean isTaskBoundsInitialized() {
+ return !mTaskBounds.isEmpty();
+ }
+
+ @Nullable
+ Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ void setConfiguration(@Nullable Configuration configuration) {
+ mConfiguration = configuration;
+ }
+
+ /** Whether there is any {@link TaskFragmentContainer} below this Task. */
+ boolean isEmpty() {
+ return mContainers.isEmpty() && mFinishedContainer.isEmpty();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 72519dc6da5f..e0fda58fd664 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -21,6 +21,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -29,12 +32,12 @@ import static org.mockito.Mockito.never;
import android.app.Activity;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.window.extensions.embedding.SplitController.TaskContainer;
import org.junit.Before;
import org.junit.Test;
@@ -53,6 +56,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SplitControllerTest {
private static final int TASK_ID = 10;
+ private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
@Mock
private Activity mActivity;
@@ -70,8 +74,11 @@ public class SplitControllerTest {
mSplitPresenter = mSplitController.mPresenter;
spyOn(mSplitController);
spyOn(mSplitPresenter);
+ final Configuration activityConfig = new Configuration();
+ activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
+ activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
doReturn(mActivityResources).when(mActivity).getResources();
- doReturn(new Configuration()).when(mActivityResources).getConfiguration();
+ doReturn(activityConfig).when(mActivityResources).getConfiguration();
}
@Test
@@ -117,4 +124,20 @@ public class SplitControllerTest {
verify(mSplitController).removeContainer(tf);
verify(mActivity, never()).finish();
}
+
+ @Test
+ public void testNewContainer() {
+ // Must pass in a valid activity.
+ assertThrows(IllegalArgumentException.class, () ->
+ mSplitController.newContainer(null /* activity */, TASK_ID));
+ assertThrows(IllegalArgumentException.class, () ->
+ mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
+
+ final TaskFragmentContainer tf = mSplitController.newContainer(null, mActivity, TASK_ID);
+ final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
+
+ assertNotNull(tf);
+ assertNotNull(taskContainer);
+ assertEquals(TASK_BOUNDS, taskContainer.getTaskBounds());
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
new file mode 100644
index 000000000000..9fb08dffbab8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskContainer}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:TaskContainerTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskContainerTest {
+ private static final int TASK_ID = 10;
+ private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
+
+ @Test
+ public void testIsTaskBoundsInitialized() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertFalse(taskContainer.isTaskBoundsInitialized());
+
+ taskContainer.setTaskBounds(TASK_BOUNDS);
+
+ assertTrue(taskContainer.isTaskBoundsInitialized());
+ }
+
+ @Test
+ public void testSetTaskBounds() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertFalse(taskContainer.setTaskBounds(new Rect()));
+
+ assertTrue(taskContainer.setTaskBounds(TASK_BOUNDS));
+
+ assertFalse(taskContainer.setTaskBounds(TASK_BOUNDS));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertTrue(taskContainer.isEmpty());
+
+ final TaskFragmentContainer tf = new TaskFragmentContainer(null, TASK_ID);
+ taskContainer.mContainers.add(tf);
+
+ assertFalse(taskContainer.isEmpty());
+
+ taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
+ taskContainer.mContainers.clear();
+
+ assertFalse(taskContainer.isEmpty());
+ }
+}
diff --git a/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml b/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml
new file mode 100644
index 000000000000..39c3fe6f6106
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_custom_close_bg.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid
+ android:color="@color/pip_custom_close_bg" />
+ <size
+ android:width="@dimen/pip_custom_close_bg_size"
+ android:height="@dimen/pip_custom_close_bg_size" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
index a733b31d9fb0..b51dd6a00815 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
@@ -22,6 +22,14 @@
android:forceHasOverlappingRendering="false">
<ImageView
+ android:id="@+id/custom_close_bg"
+ android:layout_width="@dimen/pip_custom_close_bg_size"
+ android:layout_height="@dimen/pip_custom_close_bg_size"
+ android:layout_gravity="center"
+ android:src="@drawable/pip_custom_close_bg"
+ android:visibility="gone"/>
+
+ <ImageView
android:id="@+id/image"
android:layout_width="@dimen/pip_action_inner_size"
android:layout_height="@dimen/pip_action_inner_size"
diff --git a/libs/WindowManager/Shell/res/layout/split_decor.xml b/libs/WindowManager/Shell/res/layout/split_decor.xml
index 9ffa5e8aa179..dfb90affe7f6 100644
--- a/libs/WindowManager/Shell/res/layout/split_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/split_decor.xml
@@ -20,9 +20,10 @@
android:layout_width="match_parent">
<ImageView android:id="@+id/split_resizing_icon"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_height="@*android:dimen/starting_surface_icon_size"
+ android:layout_width="@*android:dimen/starting_surface_icon_size"
android:layout_gravity="center"
+ android:scaleType="fitCenter"
android:padding="0dp"
android:visibility="gone"
android:background="@null"/>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 4606d24d1716..6e750a3d5e34 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -30,6 +30,9 @@
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
+ <!-- PiP -->
+ <color name="pip_custom_close_bg">#D93025</color>
+
<!-- Compat controls UI -->
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
@@ -47,4 +50,4 @@
<color name="splash_screen_bg_light">#FFFFFF</color>
<color name="splash_screen_bg_dark">#000000</color>
<color name="splash_window_background_default">@color/splash_screen_bg_light</color>
-</resources> \ No newline at end of file
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index d416c060c86c..8ba41ab60c87 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -46,6 +46,10 @@
<!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
<bool name="config_pipEnableEnterSplitButton">false</bool>
+ <!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself
+ if a custom action is present before closing it. -->
+ <integer name="config_pipForceCloseDelay">1000</integer>
+
<!-- Animation duration when using long press on recents to dock -->
<integer name="long_press_dock_anim_duration">250</integer>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index a2f9e884b37d..c21381d1486a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -78,6 +78,9 @@
WindowConfiguration#PINNED_WINDOWING_MODE_ELEVATION_IN_DIP -->
<dimen name="pip_shadow_radius">5dp</dimen>
+ <!-- The width and height of the background for custom action in PiP menu. -->
+ <dimen name="pip_custom_close_bg_size">32dp</dimen>
+
<dimen name="dismiss_target_x_size">24dp</dimen>
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 4b7950e9090a..255e4d2c0d44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.animation
import android.util.ArrayMap
import android.util.Log
import android.view.View
+import androidx.dynamicanimation.animation.AnimationHandler
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FlingAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -123,6 +124,12 @@ class PhysicsAnimator<T> private constructor (target: T) {
private var defaultFling: FlingConfig = globalDefaultFling
/**
+ * AnimationHandler to use if it need custom AnimationHandler, if this is null, it will use
+ * the default AnimationHandler in the DynamicAnimation.
+ */
+ private var customAnimationHandler: AnimationHandler? = null
+
+ /**
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
* just one permanent update and end listener to the DynamicAnimations.
@@ -446,6 +453,14 @@ class PhysicsAnimator<T> private constructor (target: T) {
this.defaultFling = defaultFling
}
+ /**
+ * Set the custom AnimationHandler for all aniatmion in this animator. Set this with null for
+ * restoring to default AnimationHandler.
+ */
+ fun setCustomAnimationHandler(handler: AnimationHandler) {
+ this.customAnimationHandler = handler
+ }
+
/** Starts the animations! */
fun start() {
startAction()
@@ -495,10 +510,13 @@ class PhysicsAnimator<T> private constructor (target: T) {
// springs) on this property before flinging.
cancel(animatedProperty)
+ // Apply the custom animation handler if it not null
+ val flingAnim = getFlingAnimation(animatedProperty, target)
+ flingAnim.animationHandler =
+ customAnimationHandler ?: flingAnim.animationHandler
+
// Apply the configuration and start the animation.
- getFlingAnimation(animatedProperty, target)
- .also { flingConfig.applyToAnimation(it) }
- .start()
+ flingAnim.also { flingConfig.applyToAnimation(it) }.start()
}
}
@@ -510,6 +528,21 @@ class PhysicsAnimator<T> private constructor (target: T) {
if (flingConfig == null) {
// Apply the configuration and start the animation.
val springAnim = getSpringAnimation(animatedProperty, target)
+
+ // If customAnimationHander is exist and has not been set to the animation,
+ // it should set here.
+ if (customAnimationHandler != null &&
+ springAnim.animationHandler != customAnimationHandler) {
+ // Cancel the animation before set animation handler
+ if (springAnim.isRunning) {
+ cancel(animatedProperty)
+ }
+ // Apply the custom animation handler if it not null
+ springAnim.animationHandler =
+ customAnimationHandler ?: springAnim.animationHandler
+ }
+
+ // Apply the configuration and start the animation.
springConfig.applyToAnimation(springAnim)
animationStartActions.add(springAnim::start)
} else {
@@ -564,10 +597,13 @@ class PhysicsAnimator<T> private constructor (target: T) {
}
}
+ // Apply the custom animation handler if it not null
+ val springAnim = getSpringAnimation(animatedProperty, target)
+ springAnim.animationHandler =
+ customAnimationHandler ?: springAnim.animationHandler
+
// Apply the configuration and start the spring animation.
- getSpringAnimation(animatedProperty, target)
- .also { springConfig.applyToAnimation(it) }
- .start()
+ springAnim.also { springConfig.applyToAnimation(it) }.start()
}
}
})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 79d795ee613f..fedb9983a65e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -423,8 +423,9 @@ public class DisplayLayout {
}
final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
- info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
- info.getCutoutSpec(), rotation, info.getScale());
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getStableDisplayWidth(),
+ info.getStableDisplayHeight(), info.getDensity(), info.getCutoutSpec(), rotation,
+ info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
return computeSafeInsets(
DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? displayHeight : displayWidth,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
new file mode 100644
index 000000000000..fd3aa05cfc06
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
@@ -0,0 +1,63 @@
+/*
+ * 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.wm.shell.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.internal.jank.InteractionJankMonitor;
+
+/** Utils class for simplfy InteractionJank trancing call */
+public class InteractionJankMonitorUtils {
+
+ /**
+ * Begin a trace session.
+ *
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param view the view to trace
+ * @param tag the tag to distinguish different flow of same type CUJ.
+ */
+ public static void beginTracing(@InteractionJankMonitor.CujType int cujType,
+ @NonNull View view, @Nullable String tag) {
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withView(cujType, view);
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ InteractionJankMonitor.getInstance().begin(builder);
+ }
+
+ /**
+ * End a trace session.
+ *
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ */
+ public static void endTracing(@InteractionJankMonitor.CujType int cujType) {
+ InteractionJankMonitor.getInstance().end(cujType);
+ }
+
+ /**
+ * Cancel the trace session.
+ *
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ */
+ public static void cancelTracing(@InteractionJankMonitor.CujType int cujType) {
+ InteractionJankMonitor.getInstance().cancel(cujType);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index e270edb800bd..d5875c03ccd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -221,7 +221,8 @@ public class SystemWindows {
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
- new SurfaceControlViewHost(view.getContext(), display, wwm);
+ new SurfaceControlViewHost(
+ view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ec81d230fae7..0b8e631068fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -55,6 +55,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.wm.shell.R;
@@ -62,6 +63,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.InteractionJankMonitorUtils;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import java.io.PrintWriter;
@@ -434,6 +436,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitLayoutHandler.onLayoutSizeChanged(this);
return;
}
+ InteractionJankMonitorUtils.beginTracing(InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE,
+ mSplitWindowManager.getDividerView(), "Divider fling");
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
@@ -446,6 +450,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
if (flingFinishedCallback != null) {
flingFinishedCallback.run();
}
+ InteractionJankMonitorUtils.endTracing(
+ InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 833d9d50701c..864b9a7528b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -37,6 +37,7 @@ import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
+import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -170,6 +171,10 @@ public final class SplitWindowManager extends WindowlessWindowManager {
mDividerView.setInteractive(interactive);
}
+ View getDividerView() {
+ return mDividerView;
+ }
+
/**
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
* feasible.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index 5a94fb65f174..8f9636c0bb30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.dagger;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
+import android.animation.AnimationHandler;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -28,9 +29,11 @@ import android.os.Trace;
import androidx.annotation.Nullable;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ExternalMainThread;
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -168,4 +171,28 @@ public abstract class WMShellConcurrencyModule {
shellSplashscreenThread.start();
return new HandlerExecutor(shellSplashscreenThread.getThreadHandler());
}
+
+ /**
+ * Provide a Shell main-thread AnimationHandler. The AnimationHandler can be set on
+ * {@link android.animation.ValueAnimator}s and will ensure that the animation will run on
+ * the Shell main-thread with the SF vsync.
+ */
+ @WMSingleton
+ @Provides
+ @ChoreographerSfVsync
+ public static AnimationHandler provideShellMainExecutorSfVsyncAnimationHandler(
+ @ShellMainThread ShellExecutor mainExecutor) {
+ try {
+ AnimationHandler handler = new AnimationHandler();
+ mainExecutor.executeBlocking(() -> {
+ // This is called on the animation thread since it calls
+ // Choreographer.getSfInstance() which returns a thread-local Choreographer instance
+ // that uses the SF vsync
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ });
+ return handler;
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to initialize SfVsync animation handler in 1s", e);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 0b196f0ad7e3..e43f4fc34adf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import android.animation.AnimationHandler;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
@@ -41,6 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -182,10 +184,11 @@ public class WMShellModule {
DisplayImeController displayImeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
return new LegacySplitScreenController(context, displayController, systemWindows,
displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
- taskStackListener, transitions, mainExecutor);
+ taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 38870bcb3383..0cea36ed48c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -28,6 +28,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -110,10 +111,12 @@ public class DropZoneView extends FrameLayout {
mColorDrawable = new ColorDrawable();
setBackgroundDrawable(mColorDrawable);
+ final int iconSize = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.starting_surface_icon_size);
mSplashScreenView = new ImageView(context);
- mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
- addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ mSplashScreenView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ addView(mSplashScreenView,
+ new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
mSplashScreenView.setAlpha(0f);
mMarginView = new MarginView(context);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index 9754a0369b67..73be2835d2cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -25,6 +25,7 @@ import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR
import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
+import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -140,6 +141,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private DividerImeController mImeController;
private DividerCallbacks mCallback;
+ private AnimationHandler mSfVsyncAnimationHandler;
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
private boolean mExitAnimationRunning;
@@ -260,6 +262,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
}
+ public void setAnimationHandler(AnimationHandler sfVsyncAnimationHandler) {
+ mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -651,6 +657,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
});
mCurrentAnimator = anim;
+ mCurrentAnimator.setAnimationHandler(mSfVsyncAnimationHandler);
return anim;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index 8b6679273bea..67e487de0993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.animation.AnimationHandler;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -81,6 +82,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
private final DividerState mDividerState = new DividerState();
private final ForcedResizableInfoActivityController mForcedResizableController;
private final ShellExecutor mMainExecutor;
+ private final AnimationHandler mSfVsyncAnimationHandler;
private final LegacySplitScreenTaskListener mSplits;
private final SystemWindows mSystemWindows;
final TransactionPool mTransactionPool;
@@ -116,12 +118,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
DisplayImeController imeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
mContext = context;
mDisplayController = displayController;
mSystemWindows = systemWindows;
mImeController = imeController;
mMainExecutor = mainExecutor;
+ mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
mForcedResizableController = new ForcedResizableInfoActivityController(context, this,
mainExecutor);
mTransactionPool = transactionPool;
@@ -311,6 +314,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+ mView.setAnimationHandler(mSfVsyncAnimationHandler);
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController,
mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 95bb65c5873e..d357655882ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
+import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -270,15 +271,14 @@ public class PipAnimationController {
mEndValue = endValue;
addListener(this);
addUpdateListener(this);
- mSurfaceControlTransactionFactory =
- new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mTransitionDirection = TRANSITION_DIRECTION_NONE;
}
@Override
public void onAnimationStart(Animator animation) {
mCurrentValue = mStartValue;
- onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
+ onStartTransaction(mLeash, newSurfaceControlTransaction());
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this);
}
@@ -286,16 +286,14 @@ public class PipAnimationController {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- applySurfaceControlTransaction(mLeash,
- mSurfaceControlTransactionFactory.getTransaction(),
+ applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
animation.getAnimatedFraction());
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
onEndTransaction(mLeash, tx, mTransitionDirection);
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
@@ -342,8 +340,7 @@ public class PipAnimationController {
}
PipTransitionAnimator<T> setUseContentOverlay(Context context) {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
if (mContentOverlay != null) {
// remove existing content overlay if there is any.
tx.remove(mContentOverlay);
@@ -418,7 +415,7 @@ public class PipAnimationController {
void setDestinationBounds(Rect destinationBounds) {
mDestinationBounds.set(destinationBounds);
if (mAnimationType == ANIM_TYPE_ALPHA) {
- onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
+ onStartTransaction(mLeash, newSurfaceControlTransaction());
}
}
@@ -453,6 +450,16 @@ public class PipAnimationController {
mEndValue = endValue;
}
+ /**
+ * @return {@link SurfaceControl.Transaction} instance with vsync-id.
+ */
+ protected SurfaceControl.Transaction newSurfaceControlTransaction() {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ return tx;
+ }
+
@VisibleForTesting
public void setSurfaceControlTransactionFactory(
PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index b349010be1fe..c6e48f53681c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.view.Choreographer;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
@@ -225,18 +224,4 @@ public class PipSurfaceTransactionHelper {
public interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
-
- /**
- * Implementation of {@link SurfaceControlTransactionFactory} that returns
- * {@link SurfaceControl.Transaction} with VsyncId being set.
- */
- public static class VsyncSurfaceControlTransactionFactory
- implements SurfaceControlTransactionFactory {
- @Override
- public SurfaceControl.Transaction getTransaction() {
- final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
- return tx;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9dc861c72510..fbdf6f0b539f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -280,8 +280,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
- mSurfaceControlTransactionFactory =
- new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index 9c23a32a7d2b..513ebba59258 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -110,7 +110,10 @@ public class PipUiEventLogger {
PICTURE_IN_PICTURE_STASH_RIGHT(711),
@UiEvent(doc = "User taps on the settings button in PiP menu")
- PICTURE_IN_PICTURE_SHOW_SETTINGS(933);
+ PICTURE_IN_PICTURE_SHOW_SETTINGS(933),
+
+ @UiEvent(doc = "Closes PiP with app-provided close action")
+ PICTURE_IN_PICTURE_CUSTOM_CLOSE(1058);
private final int mId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index f73b81e9d3f3..bbec4eccce3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -121,6 +121,7 @@ public class PhonePipMenuController implements PipMenuController {
private final Optional<SplitScreenController> mSplitScreenController;
private final PipUiEventLogger mPipUiEventLogger;
private ParceledListSlice<RemoteAction> mAppActions;
+ private RemoteAction mCloseAction;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
private int mMenuState;
@@ -171,7 +172,7 @@ public class PhonePipMenuController implements PipMenuController {
detachPipMenuView();
}
- private void attachPipMenuView() {
+ void attachPipMenuView() {
// In case detach was not called (e.g. PIP unexpectedly closed)
if (mPipMenuView != null) {
detachPipMenuView();
@@ -459,6 +460,7 @@ public class PhonePipMenuController implements PipMenuController {
public void setAppActions(ParceledListSlice<RemoteAction> appActions,
RemoteAction closeAction) {
mAppActions = appActions;
+ mCloseAction = closeAction;
updateMenuActions();
}
@@ -490,9 +492,8 @@ public class PhonePipMenuController implements PipMenuController {
private void updateMenuActions() {
if (mPipMenuView != null) {
final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
- if (menuActions != null) {
- mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList());
- }
+ mPipMenuView.setActions(mPipBoundsState.getBounds(),
+ menuActions == null ? null : menuActions.getList(), mCloseAction);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 175a2445f28d..272331b7cd3f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -521,6 +521,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
};
if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
+ mMenuController.attachPipMenuView();
// Calculate the snap fraction of the current stack along the old movement bounds
final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 11633a91e8c6..a0e22011b5d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -220,10 +220,16 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
return;
}
+ final SurfaceControl targetViewLeash =
+ mTargetViewContainer.getViewRootImpl().getSurfaceControl();
+ if (!targetViewLeash.isValid()) {
+ // The surface of mTargetViewContainer is somehow not ready, bail early
+ return;
+ }
+
// Put the dismiss target behind the task
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mTargetViewContainer.getViewRootImpl().getSurfaceControl(),
- mTaskLeash, -1);
+ t.setRelativeLayer(targetViewLeash, mTaskLeash, -1);
t.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 5ddb534b6829..0f3ff36601fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -146,10 +146,11 @@ public class PipInputConsumer {
"%s: Failed to create input consumer, %s", TAG, e);
}
mMainExecutor.execute(() -> {
- // Choreographer.getInstance() must be called on the thread that the input event
+ // Choreographer.getSfInstance() must be called on the thread that the input event
// receiver should be receiving events
+ // TODO(b/222697646): remove getSfInstance usage and use vsyncId for transactions
mInputEventReceiver = new InputEventReceiver(inputChannel,
- Looper.myLooper(), Choreographer.getInstance());
+ Looper.myLooper(), Choreographer.getSfInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java
index f11ae422e837..7f84500e8406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -30,6 +31,7 @@ import com.android.wm.shell.R;
*/
public class PipMenuActionView extends FrameLayout {
private ImageView mImageView;
+ private View mCustomCloseBackground;
public PipMenuActionView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -39,10 +41,16 @@ public class PipMenuActionView extends FrameLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = findViewById(R.id.image);
+ mCustomCloseBackground = findViewById(R.id.custom_close_bg);
}
/** pass through to internal {@link #mImageView} */
public void setImageDrawable(Drawable drawable) {
mImageView.setImageDrawable(drawable);
}
+
+ /** pass through to internal {@link #mCustomCloseBackground} */
+ public void setCustomCloseBackgroundVisibility(@View.Visibility int visibility) {
+ mCustomCloseBackground.setVisibility(visibility);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index c0fa8c0a8898..6390c8984dac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -33,8 +33,10 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
+import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -72,6 +74,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
/**
@@ -113,6 +116,7 @@ public class PipMenuView extends FrameLayout {
private boolean mFocusedTaskAllowSplitScreen;
private final List<RemoteAction> mActions = new ArrayList<>();
+ private RemoteAction mCloseAction;
private AccessibilityManager mAccessibilityManager;
private Drawable mBackgroundDrawable;
@@ -151,6 +155,9 @@ public class PipMenuView extends FrameLayout {
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
+ // How long the shell will wait for the app to close the PiP if a custom action is set.
+ private final int mPipForceCloseDelay;
+
public PipMenuView(Context context, PhonePipMenuController controller,
ShellExecutor mainExecutor, Handler mainHandler,
Optional<SplitScreenController> splitScreenController,
@@ -166,6 +173,9 @@ public class PipMenuView extends FrameLayout {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
+ mPipForceCloseDelay = context.getResources().getInteger(
+ R.integer.config_pipForceCloseDelay);
+
mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background);
mBackgroundDrawable.setAlpha(0);
mViewRoot = findViewById(R.id.background);
@@ -437,9 +447,13 @@ public class PipMenuView extends FrameLayout {
return new Size(width, height);
}
- void setActions(Rect stackBounds, List<RemoteAction> actions) {
+ void setActions(Rect stackBounds, @Nullable List<RemoteAction> actions,
+ @Nullable RemoteAction closeAction) {
mActions.clear();
- mActions.addAll(actions);
+ if (actions != null && !actions.isEmpty()) {
+ mActions.addAll(actions);
+ }
+ mCloseAction = closeAction;
if (mMenuState == MENU_STATE_FULL) {
updateActionViews(mMenuState, stackBounds);
}
@@ -492,6 +506,8 @@ public class PipMenuView extends FrameLayout {
final RemoteAction action = mActions.get(i);
final PipMenuActionView actionView =
(PipMenuActionView) mActionsGroup.getChildAt(i);
+ final boolean isCloseAction = mCloseAction != null && Objects.equals(
+ mCloseAction.getActionIntent(), action.getActionIntent());
// TODO: Check if the action drawable has changed before we reload it
action.getIcon().loadDrawableAsync(mContext, d -> {
@@ -500,16 +516,12 @@ public class PipMenuView extends FrameLayout {
actionView.setImageDrawable(d);
}
}, mMainHandler);
+ actionView.setCustomCloseBackgroundVisibility(
+ isCloseAction ? View.VISIBLE : View.GONE);
actionView.setContentDescription(action.getContentDescription());
if (action.isEnabled()) {
- actionView.setOnClickListener(v -> {
- try {
- action.getActionIntent().send();
- } catch (CanceledException e) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Failed to send action, %s", TAG, e);
- }
- });
+ actionView.setOnClickListener(
+ v -> onActionViewClicked(action.getActionIntent(), isCloseAction));
}
actionView.setEnabled(action.isEnabled());
actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
@@ -559,6 +571,32 @@ public class PipMenuView extends FrameLayout {
}
}
+ /**
+ * Execute the {@link PendingIntent} attached to the {@link PipMenuActionView}.
+ * If the given {@link PendingIntent} matches {@link #mCloseAction}, we need to make sure
+ * the PiP is removed after a certain timeout in case the app does not respond in a
+ * timely manner.
+ */
+ private void onActionViewClicked(@NonNull PendingIntent intent, boolean isCloseAction) {
+ try {
+ intent.send();
+ } catch (PendingIntent.CanceledException e) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Failed to send action, %s", TAG, e);
+ }
+ if (isCloseAction) {
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_CUSTOM_CLOSE);
+ mAllowTouches = false;
+ mMainExecutor.executeDelayed(() -> {
+ hideMenu();
+ // TODO: it's unsafe to call onPipDismiss with a delay here since
+ // we may have a different PiP by the time this runnable is executed.
+ mController.onPipDismiss();
+ mAllowTouches = true;
+ }, mPipForceCloseDelay);
+ }
+ }
+
private void enterSplit() {
// Do not notify menu visibility when hiding the menu, the controller will do this when it
// handles the message
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 7028f9a25fbe..fa0f0925a08a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -33,6 +33,11 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.Looper;
+import android.view.Choreographer;
+
+import androidx.dynamicanimation.animation.AnimationHandler;
+import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -84,6 +89,26 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
+ private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ ThreadLocal.withInitial(() -> {
+ final Looper initialLooper = Looper.myLooper();
+ final FrameCallbackScheduler scheduler = new FrameCallbackScheduler() {
+ @Override
+ public void postFrameCallback(@androidx.annotation.NonNull Runnable runnable) {
+ // TODO(b/222697646): remove getSfInstance usage and use vsyncId for
+ // transactions
+ Choreographer.getSfInstance().postFrameCallback(t -> runnable.run());
+ }
+
+ @Override
+ public boolean isCurrentThread() {
+ return Looper.myLooper() == initialLooper;
+ }
+ };
+ AnimationHandler handler = new AnimationHandler(scheduler);
+ return handler;
+ });
+
/**
* PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
* using physics animations.
@@ -186,8 +211,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
public void init() {
+ // Note: Needs to get the shell main thread sf vsync animation handler
mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+ mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
+ mSfAnimationHandlerThreadLocal.get());
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 89d85e4b292d..abf1a9500e6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -625,7 +625,8 @@ public class PipResizeGestureHandler {
class PipResizeInputEventReceiver extends BatchedInputEventReceiver {
PipResizeInputEventReceiver(InputChannel channel, Looper looper) {
- super(channel, looper, Choreographer.getInstance());
+ // TODO(b/222697646): remove getSfInstance usage and use vsyncId for transactions
+ super(channel, looper, Choreographer.getSfInstance());
}
public void onInputEvent(InputEvent event) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 8da6224d990c..37e9344348d9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -25,6 +25,8 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,9 +83,19 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
/** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ super.pipLayerExpands()
+ }
+
@Presubmit
@Test
- override fun pipLayerExpands() = super.pipLayerExpands()
+ fun pipLayerExpands_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ super.pipLayerExpands()
+ }
companion object {
/**
diff --git a/media/Android.bp b/media/Android.bp
index 3516d25fb17f..b45d69489add 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -120,6 +120,13 @@ aidl_interface {
],
},
},
+ versions_with_info: [
+ {
+ version: "1",
+ imports: [],
+ },
+ ],
+
}
aidl_interface {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 5e300c8b1d9c..75fd64a4ccf5 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3653,6 +3653,7 @@ public final class MediaCodecInfo {
.parseIntRange(info.getString("quality-range"), mQualityRange);
}
if (info.containsKey("feature-bitrate-modes")) {
+ mBitControl = 0;
for (String mode: info.getString("feature-bitrate-modes").split(",")) {
mBitControl |= (1 << parseBitrateMode(mode));
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 9dea5b9152b7..32fff1eb270d 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1247,7 +1247,7 @@ public final class MediaFormat {
* A key describing the per-frame average block QP (Quantization Parameter).
* This is a part of a video 'Encoding Statistics' export feature.
* This value is emitted from video encoder for a video frame.
- * The average value is rounded down (using floor()) to integer value.
+ * The average value is rounded to the nearest integer value.
*
* The associated value is an integer.
*/
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 65428de95519..d627984c7fff 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -184,13 +184,13 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano
mTimestampsNanos.push_back(now);
/**
- * Use current sample to determine the rate limit. We can pick a shorter rate limit
- * if any sample underperformed, however, it could be the lower level system is slow
- * to react. So here we explicitly choose the rate limit with the latest sample.
+ * Cache the hint if the hint is not overtime and the mLastUpdateTimestamp is
+ * still in the mPreferredRateNanos duration.
*/
- int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos
- : 10 * mPreferredRateNanos;
- if (now - mLastUpdateTimestamp <= rateLimit) return 0;
+ if (actualDurationNanos < mTargetDurationNanos &&
+ now - mLastUpdateTimestamp <= mPreferredRateNanos) {
+ return 0;
+ }
binder::Status ret =
mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index d80317bb8c60..58bcd1d49c69 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -29,4 +29,11 @@ aidl_interface {
enabled: true,
},
},
+ versions_with_info: [
+ {
+ version: "1",
+ imports: [],
+ },
+ ],
+
}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/.hash b/omapi/aidl/aidl_api/android.se.omapi/1/.hash
new file mode 100644
index 000000000000..f77182a769b3
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/.hash
@@ -0,0 +1 @@
+894069bcfe4f35ceb2088278ddf87c83adee8014
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 000000000000..87fdac0443c9
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementChannel {
+ void close();
+ boolean isClosed();
+ boolean isBasicChannel();
+ byte[] getSelectResponse();
+ byte[] transmit(in byte[] command);
+ boolean selectNext();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 000000000000..77e1c53f47ac
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementListener {
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 000000000000..2b10c473c902
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementReader {
+ boolean isSecureElementPresent();
+ android.se.omapi.ISecureElementSession openSession();
+ void closeSessions();
+ boolean reset();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 000000000000..0c8e431d18a1
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementService {
+ String[] getReaders();
+ android.se.omapi.ISecureElementReader getReader(in String reader);
+ boolean[] isNfcEventAllowed(in String reader, in byte[] aid, in String[] packageNames, in int userId);
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 000000000000..06287c551f5c
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/1/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementSession {
+ byte[] getAtr();
+ void close();
+ void closeChannels();
+ boolean isClosed();
+ android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+ android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
new file mode 100644
index 000000000000..a0c2698da4b5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.wifi;
+
+import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
+import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a singleton class for Wi-Fi state worker.
+ */
+public class WifiStateWorker {
+
+ private static final String TAG = "WifiStateWorker";
+ private static final Object sLock = new Object();
+
+ /**
+ * A singleton {@link WifiStateWorker} object is used to share with all sub-settings.
+ */
+ @GuardedBy("sLock")
+ private static WifiStateWorker sInstance;
+ @TestApi
+ @GuardedBy("sLock")
+ private static Map<Context, WifiStateWorker> sTestInstances;
+
+ @VisibleForTesting
+ static WifiManager sWifiManager;
+ private static int sWifiState;
+ private static HandlerThread sWorkerThread;
+
+ /**
+ * Static method to create a singleton class for WifiStateWorker.
+ *
+ * @param context The Context this is associated with.
+ * @return an instance of {@link WifiStateWorker} object.
+ */
+ @NonNull
+ @AnyThread
+ public static WifiStateWorker getInstance(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sTestInstances != null && sTestInstances.containsKey(context)) {
+ WifiStateWorker testInstance = sTestInstances.get(context);
+ Log.w(TAG, "The context owner try to use a test instance:" + testInstance);
+ return testInstance;
+ }
+
+ if (sInstance != null) return sInstance;
+
+ sInstance = new WifiStateWorker();
+ sWorkerThread = new HandlerThread(
+ TAG + ":{" + context.getApplicationInfo().className + "}",
+ Process.THREAD_PRIORITY_DISPLAY);
+ sWorkerThread.start();
+ sWorkerThread.getThreadHandler().post(() -> init(context));
+ return sInstance;
+ }
+ }
+
+ /**
+ * A convenience method to set pre-prepared instance or mock(WifiStateWorker.class) for testing.
+ *
+ * @param context The Context this is associated with.
+ * @param instance of {@link WifiStateWorker} object.
+ * @hide
+ */
+ @TestApi
+ @VisibleForTesting
+ public static void setTestInstance(@NonNull Context context, WifiStateWorker instance) {
+ synchronized (sLock) {
+ if (sTestInstances == null) sTestInstances = new ConcurrentHashMap<>();
+
+ Log.w(TAG, "Try to set a test instance by context:" + context);
+ sTestInstances.put(context, instance);
+ }
+ }
+
+ @WorkerThread
+ private static void init(@NonNull Context context) {
+ final Context appContext = context.getApplicationContext();
+ final IntentReceiver receiver = new IntentReceiver();
+ appContext.registerReceiver(receiver, new IntentFilter(WIFI_STATE_CHANGED_ACTION), null,
+ sWorkerThread.getThreadHandler());
+ sWifiManager = appContext.getSystemService(WifiManager.class);
+ refresh();
+ }
+
+ /**
+ * Refresh Wi-Fi state with WifiManager#getWifiState()
+ */
+ @AnyThread
+ public static void refresh() {
+ if (sWifiManager == null) return;
+ Log.d(TAG, "Start calling WifiManager#getWifiState.");
+ sWifiState = sWifiManager.getWifiState();
+ Log.d(TAG, "WifiManager#getWifiState return state:" + sWifiState);
+ }
+
+ /**
+ * Gets the Wi-Fi enabled state.
+ *
+ * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
+ * {@link WifiManager#WIFI_STATE_DISABLING}, {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING}, {@link WifiManager#WIFI_STATE_UNKNOWN}
+ * @see #isWifiEnabled()
+ */
+ @AnyThread
+ public int getWifiState() {
+ return sWifiState;
+ }
+
+ /**
+ * Return whether Wi-Fi is enabled or disabled.
+ *
+ * @return {@code true} if Wi-Fi is enabled
+ * @see #getWifiState()
+ */
+ @AnyThread
+ public boolean isWifiEnabled() {
+ return sWifiState == WIFI_STATE_ENABLED;
+ }
+
+ @WorkerThread
+ private static class IntentReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ sWifiState = intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_DISABLED);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java
new file mode 100644
index 000000000000..2589a90e33c2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.UserHandle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+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.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiStateWorkerTest {
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ WifiManager mWifiManager;
+
+ WifiStateWorker mWifiStateWorker;
+
+ @Before
+ public void setUp() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+ WifiStateWorker.sWifiManager = mWifiManager;
+
+ mWifiStateWorker = WifiStateWorker.getInstance(mContext);
+ }
+
+ @Test
+ public void getInstance_diffContext_getSameInstance() {
+ Context context = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+ WifiStateWorker instance = WifiStateWorker.getInstance(context);
+
+ assertThat(mContext).isNotEqualTo(context);
+ assertThat(mWifiStateWorker).isEqualTo(instance);
+ }
+
+ @Test
+ public void getWifiState_wifiStateDisabling_returnWifiStateDisabling() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLING);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLING);
+ }
+
+ @Test
+ public void getWifiState_wifiStateDisabled_returnWifiStateDisabled() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLED);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLED);
+ }
+
+ @Test
+ public void getWifiState_wifiStateEnabling_returnWifiStateEnabling() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLING);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLING);
+ }
+
+ @Test
+ public void getWifiState_wifiStateEnabled_returnWifiStateEnabled() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLED);
+ }
+
+ @Test
+ public void isWifiEnabled_wifiStateDisabling_returnFalse() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLING);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.isWifiEnabled()).isFalse();
+ }
+
+ @Test
+ public void isWifiEnabled_wifiStateDisabled_returnFalse() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLED);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.isWifiEnabled()).isFalse();
+ }
+
+ @Test
+ public void isWifiEnabled_wifiStateEnabling_returnFalse() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLING);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.isWifiEnabled()).isFalse();
+ }
+
+ @Test
+ public void isWifiEnabled_wifiStateEnabled_returnTrue() {
+ when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+ WifiStateWorker.refresh();
+
+ assertThat(mWifiStateWorker.isWifiEnabled()).isTrue();
+ }
+}
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 6c8a92d9485c..4b07eaf780e4 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -73,6 +73,7 @@ xuqiu@google.com
zakcohen@google.com
jernej@google.com
jglazier@google.com
+peskal@google.com
#Android Auto
hseog@google.com
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 58adb9146bd0..e1b294f2d757 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -21,6 +21,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="@integer/biometric_dialog_text_gravity"
+ android:singleLine="true"
+ android:marqueeRepeatLimit="1"
+ android:ellipsize="marquee"
style="@style/TextAppearance.AuthCredential.Title"/>
<TextView
@@ -28,13 +31,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="@integer/biometric_dialog_text_gravity"
+ android:singleLine="true"
+ android:marqueeRepeatLimit="1"
+ android:ellipsize="marquee"
style="@style/TextAppearance.AuthCredential.Subtitle"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
+ android:scrollbars ="vertical"
style="@style/TextAppearance.AuthCredential.Description"/>
<Space android:id="@+id/space_above_icon"
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
deleted file mode 100644
index ca5c4996cddb..000000000000
--- a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/dream_preview_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="@dimen/dream_overlay_complication_preview_text_size"
- android:textColor="@android:color/white"
- android:shadowColor="@color/keyguard_shadow_color"
- android:shadowRadius="?attr/shadowRadius"
- android:gravity="center_vertical"
- android:drawableStart="@drawable/dream_preview_back_arrow"
- android:drawablePadding="@dimen/dream_overlay_complication_preview_icon_padding"/>
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 52132e881c43..50d3cc4e8a7a 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -48,7 +48,7 @@
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginBottom="4dp"
- android:tint="?android:attr/textColor"
+ android:tint="@color/media_paging_indicator"
android:forceHasOverlappingRendering="false"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_long_press_menu.xml b/packages/SystemUI/res/layout/media_long_press_menu.xml
new file mode 100644
index 000000000000..99c5e4707338
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_long_press_menu.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="0dp"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:id="@+id/remove_text"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:text="@string/controls_media_close_session"
+ android:gravity="center_horizontal|top"
+ app:layout_constraintTop_toBottomOf="@id/settings"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/cancel" />
+
+ <ImageButton
+ android:id="@+id/settings"
+ android:src="@drawable/ic_settings"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="4dp"
+ android:background="@drawable/qs_media_light_source"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:layout_gravity="top"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ </ImageButton>
+
+ <FrameLayout
+ android:id="@+id/dismiss"
+ android:background="@drawable/qs_media_light_source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/cancel"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
+ <TextView
+ android:id="@+id/dismiss_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|top"
+ style="@style/MediaPlayer.SolidButton"
+ android:background="@drawable/qs_media_solid_button"
+ android:text="@string/controls_media_dismiss_button" />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/cancel"
+ android:background="@drawable/qs_media_light_source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
+ app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
+ app:layout_constraintStart_toEndOf="@id/dismiss"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
+ <TextView
+ android:id="@+id/cancel_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|top"
+ style="@style/MediaPlayer.OutlineButton"
+ android:text="@string/cancel" />
+ </FrameLayout>
+
+</merge> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 665edac65afc..7962e22d6b7f 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -300,87 +300,7 @@
android:layout_marginBottom="@dimen/qs_media_padding"
android:layout_marginTop="0dp" />
- <!-- Long press menu -->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="0dp"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:id="@+id/remove_text"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:text="@string/controls_media_close_session"
- android:gravity="center_horizontal|top"
- app:layout_constraintTop_toBottomOf="@id/settings"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/cancel" />
+ <include
+ layout="@layout/media_long_press_menu" />
- <ImageButton
- android:id="@+id/settings"
- android:src="@drawable/ic_settings"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:background="@drawable/qs_media_light_source"
- android:contentDescription="@string/controls_media_settings_button"
- android:layout_gravity="top"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent">
- </ImageButton>
-
- <FrameLayout
- android:id="@+id/dismiss"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
- <TextView
- android:id="@+id/dismiss_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|top"
- style="@style/MediaPlayer.SolidButton"
- android:background="@drawable/qs_media_solid_button"
- android:text="@string/controls_media_dismiss_button" />
- </FrameLayout>
- <FrameLayout
- android:id="@+id/cancel"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
- app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintStart_toEndOf="@id/dismiss"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
- <TextView
- android:id="@+id/cancel_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|top"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
- </FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 5510f24870bb..659a578aa61f 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -167,97 +167,7 @@
android:layout_marginBottom="@dimen/qs_media_padding"
/>
- <!-- Long press menu -->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:id="@+id/remove_text"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:text="@string/controls_media_close_session"
- android:gravity="center_horizontal|top"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/cancel"/>
-
- <FrameLayout
- android:id="@+id/settings"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:id="@+id/settings_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_settings_button" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/cancel"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/settings"
- app:layout_constraintEnd_toStartOf="@id/dismiss"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
- </FrameLayout>
+ <include
+ layout="@layout/media_long_press_menu" />
- <FrameLayout
- android:id="@+id/dismiss"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/cancel"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_dismiss_button"
- />
- </FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 24118d256fcc..02ba271ee9a4 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -176,6 +176,7 @@
<!-- media -->
<color name="media_seamless_border">?android:attr/colorAccent</color>
+ <color name="media_paging_indicator">@color/material_dynamic_neutral_variant80</color>
<!-- media output dialog-->
<color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ae5b8d8ae451..83b1b52fff3d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -512,9 +512,6 @@
<!-- Flag to turn on the rendering of the above path or not -->
<bool name="config_enableDisplayCutoutProtection">false</bool>
- <!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
- <bool name="config_roundedCornerMultipleRadius">false</bool>
-
<!-- Controls can query 2 preferred applications for limited number of suggested controls.
This config value should contain a list of package names of thoses preferred applications.
-->
@@ -659,17 +656,6 @@
<!-- Flag to activate notification to contents feature -->
<bool name="config_notificationToContents">true</bool>
- <!-- Respect drawable/rounded_secondary.xml intrinsic size for multiple radius corner path
- customization for secondary display-->
- <bool name="config_roundedCornerMultipleRadiusSecondary">false</bool>
-
- <!-- Whether the rounded corners are multiple radius for each display in a multi-display device.
- {@see com.android.internal.R.array#config_displayUniqueIdArray} -->
- <array name="config_roundedCornerMultipleRadiusArray">
- <item>@bool/config_roundedCornerMultipleRadius</item>
- <item>@bool/config_roundedCornerMultipleRadiusSecondary</item>
- </array>
-
<!-- The rounded corner drawable for each display in a multi-display device.
{@see com.android.internal.R.array#config_displayUniqueIdArray} -->
<array name="config_roundedCornerDrawableArray">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f7acda7ec379..f92d6238e863 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -966,10 +966,10 @@
<string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
- <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The first placeholder is the organization name, and the second placeholder is the VPN name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_named_management_named_vpn">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%2$s</xliff:g></string>
+ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The first placeholder is the organization name, and the second placeholder is the VPN name. [CHAR LIMIT=150] -->
+ <string name="quick_settings_disclosure_named_management_named_vpn">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> and is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%2$s</xliff:g></string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_management">This device belongs to your organization</string>
@@ -978,10 +978,10 @@
<string name="quick_settings_disclosure_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g></string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to multiple VPNs. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_management_vpns">This device belongs to your organization and is connected to VPNs</string>
+ <string name="quick_settings_disclosure_management_vpns">This device belongs to your organization and is connected to the internet through VPNs</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to multiple VPNs. The placeholder is the organization's name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_named_management_vpns">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> and is connected to VPNs</string>
+ <string name="quick_settings_disclosure_named_management_vpns">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> and is connected to the internet through VPNs</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the device has a managed profile which can be monitored by the profile owner [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_managed_profile_monitoring">Your organization may monitor network traffic in your work profile</string>
@@ -996,16 +996,19 @@
<string name="quick_settings_disclosure_monitoring">Network may be monitored</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to multiple VPNs. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_vpns">This device is connected to VPNs</string>
+ <string name="quick_settings_disclosure_vpns">This device is connected to the internet through VPNs</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN in the work profile. The placeholder is the VPN name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_managed_profile_named_vpn">Your work profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <string name="quick_settings_disclosure_managed_profile_named_vpn">Your work apps are connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN in the personal profile (instead of the work profile). The placeholder is the VPN name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_personal_profile_named_vpn">Your personal profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <string name="quick_settings_disclosure_personal_profile_named_vpn">Your personal apps are connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
- <string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <string name="quick_settings_disclosure_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+
<!-- Monitoring dialog title for financed device [CHAR LIMIT=60] -->
<string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
@@ -1054,16 +1057,16 @@
<string name="monitoring_description_managed_profile_network_logging">Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.</string>
<!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_named_vpn">You\'re connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+ <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
<!-- Monitoring dialog: Description of two active VPNs. [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_two_named_vpns">You\'re connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+ <string name="monitoring_description_two_named_vpns">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
<!-- Monitoring dialog: Description of an active VPN in the work profile. [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_managed_profile_named_vpn">Your work profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+ <string name="monitoring_description_managed_profile_named_vpn">Your work apps are connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity in work apps, including emails and browsing data, is visible to your IT admin and VPN provider.</string>
<!-- Monitoring dialog: Description of an active VPN in the personal profile (as opposed to the work profile). [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_personal_profile_named_vpn">Your personal profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+ <string name="monitoring_description_personal_profile_named_vpn">Your personal apps are connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your VPN provider.</string>
<!-- Monitoring dialog: Space that separates the VPN body text and the "Open VPN Settings" link that follows it. [CHAR LIMIT=5] -->
<string name="monitoring_description_vpn_settings_separator">" "</string>
@@ -2283,6 +2286,10 @@
<string name="media_output_broadcast_code">Password</string>
<!-- Button for change broadcast name and broadcast code [CHAR LIMIT=60] -->
<string name="media_output_broadcast_dialog_save">Save</string>
+ <!-- The "starting" text when Broadcast is starting [CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_starting">Starting&#8230;</string>
+ <!-- The button text when Broadcast start failed [CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_start_failed">Can\u2019t broadcast</string>
<!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_clip_data_label">Build number</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 938b1cae845e..88fe03465405 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -165,7 +165,7 @@ public class PipSurfaceTransactionHelper {
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
public static SurfaceControl.Transaction newSurfaceControlTransaction() {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
return tx;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 461c2dc2c2af..be3dfdcf05d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -23,6 +23,7 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.ActivityTaskManager.getService;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityManager;
@@ -154,11 +155,15 @@ public class ActivityManagerWrapper {
/**
* Removes the outdated snapshot of home task.
+ *
+ * @param homeActivity The home task activity, or null if you have the
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS} permission and
+ * want us to find the home task for you.
*/
- public void invalidateHomeTaskSnapshot(final Activity homeActivity) {
+ public void invalidateHomeTaskSnapshot(@Nullable final Activity homeActivity) {
try {
ActivityClient.getInstance().invalidateHomeTaskSnapshot(
- homeActivity.getActivityToken());
+ homeActivity == null ? null : homeActivity.getActivityToken());
} catch (Throwable e) {
Log.w(TAG, "Failed to invalidate home snapshot", e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index b82d896e6bea..f0210fd4c914 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -18,6 +18,7 @@ package com.android.systemui.shared.system;
import android.annotation.IntDef;
import android.os.Build;
+import android.text.TextUtils;
import android.view.View;
import com.android.internal.jank.InteractionJankMonitor;
@@ -46,6 +47,8 @@ public final class InteractionJankMonitorWrapper {
InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
public static final int CUJ_APP_LAUNCH_FROM_WIDGET =
InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET;
+ public static final int CUJ_SPLIT_SCREEN_ENTER =
+ InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -86,6 +89,23 @@ public final class InteractionJankMonitorWrapper {
}
/**
+ * Begin a trace session.
+ *
+ * @param v an attached view.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param tag the tag to distinguish different flow of same type CUJ.
+ */
+ public static void begin(View v, @CujType int cujType, String tag) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
+ Configuration.Builder builder =
+ Configuration.Builder.withView(cujType, v);
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ InteractionJankMonitor.getInstance().begin(builder);
+ }
+
+ /**
* End a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c0ba51f27fe2..f4355798a381 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -860,7 +860,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
private void setupUserSwitcher() {
final UserRecord currentUser = mUserSwitcherController.getCurrentUserRecord();
if (currentUser == null) {
- Log.wtf(TAG, "Current user in user switcher is null.");
+ Log.e(TAG, "Current user in user switcher is null.");
return;
}
Drawable userIcon = findUserIcon(currentUser.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 38f9ac1b4cdc..b98fc03e3acd 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -19,8 +19,6 @@ import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -38,10 +36,10 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
@@ -51,10 +49,12 @@ import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
+import android.util.DisplayUtils;
import android.util.Log;
import android.util.Size;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -320,13 +320,15 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
private void startOnScreenDecorationsThread() {
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
mRotation = mContext.getDisplay().getRotation();
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
+ mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
+ getPhysicalPixelDisplaySizeRatio());
mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
updateHwLayerRoundedCornerDrawable();
setupDecorations();
@@ -402,6 +404,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
updateOverlayProviderViews();
}
+
+ final float newRatio = getPhysicalPixelDisplaySizeRatio();
+ if (mRoundedCornerResDelegate.getPhysicalPixelDisplaySizeRatio() != newRatio) {
+ mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(newRatio);
+ if (mScreenDecorHwcLayer != null) {
+ updateHwLayerRoundedCornerExistAndSize();
+ }
+ updateOverlayProviderViews();
+ }
+
if (mCutoutViews != null) {
final int size = mCutoutViews.length;
for (int i = 0; i < size; i++) {
@@ -878,6 +890,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
}
+ @VisibleForTesting
+ float getPhysicalPixelDisplaySizeRatio() {
+ final Point stableDisplaySize = mDisplayManager.getStableDisplaySize();
+ final DisplayInfo displayInfo = new DisplayInfo();
+ mContext.getDisplay().getDisplayInfo(displayInfo);
+ return DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ stableDisplaySize.x, stableDisplaySize.y, displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -1182,25 +1204,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
private void updateBoundingPath() {
- int lw = displayInfo.logicalWidth;
- int lh = displayInfo.logicalHeight;
-
- boolean flipped = displayInfo.rotation == ROTATION_90
- || displayInfo.rotation == ROTATION_270;
-
- int dw = flipped ? lh : lw;
- int dh = flipped ? lw : lh;
-
- Path path = DisplayCutout.pathFromResources(
- getResources(), getDisplay().getUniqueId(), dw, dh);
+ final Path path = displayInfo.displayCutout.getCutoutPath();
if (path != null) {
cutoutPath.set(path);
} else {
cutoutPath.reset();
}
- Matrix m = new Matrix();
- transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m);
- cutoutPath.transform(m);
}
private void updateGravity() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index f182e772f75e..9af83003d26f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -39,10 +39,13 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;
import com.android.systemui.shared.system.SysUiStatsLog;
+import java.util.List;
+
/**
* Contains logic for an accessibility floating menu view.
*/
@@ -120,9 +123,13 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
if (isShowing()) {
return;
}
+ final List<AccessibilityTarget> targetList = getTargets(mContext, ACCESSIBILITY_BUTTON);
+ if (targetList.isEmpty()) {
+ return;
+ }
mMenuView.show();
- mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON));
+ mMenuView.onTargetsChanged(targetList);
mMenuView.updateOpacityWith(isFadeEffectEnabled(mContext),
getOpacityValue(mContext));
mMenuView.setSizeType(getSizeType(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index cc5a792e89a1..11353f67a799 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -51,26 +51,19 @@ public class AccessibilityFloatingMenuController implements
private int mBtnMode;
private String mBtnTargets;
private boolean mIsKeyguardVisible;
- private boolean mIsAccessibilityManagerServiceReady;
@VisibleForTesting
final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() {
- // Accessibility floating menu needs to retrieve information from
- // AccessibilityManagerService, and it would be ready before onUserUnlocked().
+
@Override
public void onUserUnlocked() {
- mIsAccessibilityManagerServiceReady = true;
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
}
- // Keyguard state would be changed before AccessibilityManagerService is ready to retrieve,
- // need to wait until receive onUserUnlocked().
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
mIsKeyguardVisible = showing;
- if (mIsAccessibilityManagerServiceReady) {
- handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
- }
+ handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
}
@Override
@@ -99,7 +92,6 @@ public class AccessibilityFloatingMenuController implements
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mIsKeyguardVisible = false;
- mIsAccessibilityManagerServiceReady = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 55da8da6cc33..1413f4a81574 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -34,6 +34,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -706,6 +707,12 @@ public class AuthBiometricView extends LinearLayout {
mTitleView.setText(mPromptInfo.getTitle());
+ //setSelected could make marguee work
+ mTitleView.setSelected(true);
+ mSubtitleView.setSelected(true);
+ //make description view become scrollable
+ mDescriptionView.setMovementMethod(new ScrollingMovementMethod());
+
if (isDeviceCredentialAllowed()) {
final CharSequence credentialButtonText;
@Utils.CredentialType final int credentialType =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 15d0648ae163..8e1132600a64 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -30,7 +30,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
@@ -54,6 +56,7 @@ import android.os.RemoteException;
import android.os.UserManager;
import android.util.Log;
import android.util.SparseBooleanArray;
+import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.WindowManager;
@@ -104,16 +107,16 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private final CommandQueue mCommandQueue;
private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager;
- @Nullable
- private final FingerprintManager mFingerprintManager;
- @Nullable
- private final FaceManager mFaceManager;
+ @Nullable private final FingerprintManager mFingerprintManager;
+ @Nullable private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
- @Nullable
- private final PointF mFaceAuthSensorLocation;
- @Nullable
- private PointF mFingerprintLocation;
+
+ @NonNull private Point mStableDisplaySize = new Point();
+
+ @Nullable private final PointF mFaceAuthSensorLocation;
+ @Nullable private PointF mFingerprintLocation;
+ @Nullable private Rect mUdfpsBounds;
private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState
@@ -122,14 +125,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
AuthDialog mCurrentDialog;
@NonNull private final WindowManager mWindowManager;
+ @NonNull private final DisplayManager mDisplayManager;
@Nullable private UdfpsController mUdfpsController;
@Nullable private IUdfpsHbmListener mUdfpsHbmListener;
@Nullable private SidefpsController mSidefpsController;
@Nullable private IBiometricContextListener mBiometricContextListener;
- @VisibleForTesting
- IBiometricSysuiReceiver mReceiver;
- @VisibleForTesting
- @NonNull final BiometricDisplayListener mOrientationListener;
+ @VisibleForTesting IBiometricSysuiReceiver mReceiver;
+ @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -249,6 +251,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
mAllFingerprintAuthenticatorsRegistered = true;
mFpProps = sensors;
+
List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
for (FingerprintSensorPropertiesInternal props : mFpProps) {
@@ -259,12 +262,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
sidefpsProps.add(props);
}
}
+
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
if (mUdfpsProps != null) {
mUdfpsController = mUdfpsControllerFactory.get();
mUdfpsController.addCallback(new UdfpsController.Callback() {
@Override
- public void onFingerUp() {}
+ public void onFingerUp() {
+ }
@Override
public void onFingerDown() {
@@ -273,15 +278,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
}
});
+ mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
+ mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
+ updateUdfpsLocation();
}
+
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
mSidefpsController = mSidefpsControllerFactory.get();
}
+
+ mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
+ updateFingerprintLocation();
+
for (Callback cb : mCallbacks) {
cb.onAllAuthenticatorsRegistered();
}
- mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
}
private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
@@ -424,12 +436,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
/**
* @return where the UDFPS exists on the screen in pixels in portrait mode.
*/
- @Nullable public PointF getUdfpsSensorLocation() {
- if (mUdfpsController == null) {
+ @Nullable public PointF getUdfpsLocation() {
+ if (mUdfpsController == null || mUdfpsBounds == null) {
return null;
}
- return new PointF(mUdfpsController.getSensorLocation().centerX(),
- mUdfpsController.getSensorLocation().centerY());
+ return new PointF(mUdfpsBounds.centerX(), mUdfpsBounds.centerY());
}
/**
@@ -437,8 +448,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
* overridden value will use the default value even if they don't have a fingerprint sensor
*/
@Nullable public PointF getFingerprintSensorLocation() {
- if (getUdfpsSensorLocation() != null) {
- return getUdfpsSensorLocation();
+ if (getUdfpsLocation() != null) {
+ return getUdfpsLocation();
}
return mFingerprintLocation;
}
@@ -525,12 +536,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFaceManager = faceManager;
mUdfpsControllerFactory = udfpsControllerFactory;
mSidefpsControllerFactory = sidefpsControllerFactory;
+ mDisplayManager = displayManager;
mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray();
mOrientationListener = new BiometricDisplayListener(
context,
- displayManager,
+ mDisplayManager,
mHandler,
BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> {
@@ -582,6 +594,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
yLocation);
}
+ // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
+ // This is not combined with updateFingerprintLocation because this is invoked directly from
+ // UdfpsController, only when it cares about a rotation change. The implications of calling
+ // updateFingerprintLocation in such a case are unclear.
+ private void updateUdfpsLocation() {
+ if (mUdfpsController != null) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ mContext.getDisplay().getDisplayInfo(displayInfo);
+ final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ mStableDisplaySize.x, mStableDisplaySize.y, displayInfo.getNaturalWidth(),
+ displayInfo.getNaturalHeight());
+
+ final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
+ mUdfpsBounds = udfpsProp.getLocation().getRect();
+ mUdfpsBounds.scale(scaleFactor);
+ mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
+ new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(),
+ displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation));
+ }
+ }
+
@SuppressWarnings("deprecation")
@Override
public void start() {
@@ -592,6 +625,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFingerprintAuthenticatorsRegisteredCallback);
}
+ mStableDisplaySize = mDisplayManager.getStableDisplaySize();
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
}
@@ -906,6 +940,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateFingerprintLocation();
+ updateUdfpsLocation();
// Save the state of the current dialog (buttons showing, etc)
if (mCurrentDialog != null) {
@@ -935,6 +970,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private void onOrientationChanged() {
updateFingerprintLocation();
+ updateUdfpsLocation();
if (mCurrentDialog != null) {
mCurrentDialog.onOrientationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
index dfbe348c6ede..38a7c5d7a8af 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
@@ -64,7 +64,11 @@ class BiometricDisplayListener(
/** Listen for changes. */
fun enable() {
lastRotation = context.display?.rotation ?: Surface.ROTATION_0
- displayManager.registerDisplayListener(this, handler)
+ displayManager.registerDisplayListener(
+ this,
+ handler,
+ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ )
}
/** Stop listening for changes. */
@@ -80,9 +84,7 @@ class BiometricDisplayListener(
*/
sealed class SensorType {
object Generic : SensorType()
- data class UnderDisplayFingerprint(
- val properties: FingerprintSensorPropertiesInternal
- ) : SensorType()
+ object UnderDisplayFingerprint : SensorType()
data class SideFingerprint(
val properties: FingerprintSensorPropertiesInternal
) : SensorType()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 284724687abc..0096032a6e68 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -19,7 +19,6 @@ package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
-import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
@@ -30,12 +29,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Point;
-import android.graphics.RectF;
import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.Handler;
@@ -45,6 +41,7 @@ import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.util.Log;
+import android.util.RotationUtils;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -99,7 +96,6 @@ import kotlin.Unit;
public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
- private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds
// Minimum required delay between consecutive touch logs in milliseconds.
private static final long MIN_TOUCH_LOG_INTERVAL = 50;
@@ -129,10 +125,14 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController;
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
+ @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
- @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
- @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ @VisibleForTesting int mSensorId;
+ @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
+ // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
+ @Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -193,19 +193,16 @@ public class UdfpsController implements DozeReceiver {
@Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@NonNull IUdfpsOverlayControllerCallback callback) {
- mFgExecutor.execute(
- () -> UdfpsController.this.showUdfpsOverlay(new UdfpsControllerOverlay(
- mContext, mFingerprintManager, mInflater, mWindowManager,
- mAccessibilityManager, mStatusBarStateController,
+ mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
+ new UdfpsControllerOverlay(mContext, mFingerprintManager, mInflater,
+ mWindowManager, mAccessibilityManager, mStatusBarStateController,
mPanelExpansionStateManager, mKeyguardViewManager,
mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController,
- mUnlockedScreenOffAnimationController, mSensorProps,
- mHbmProvider, requestId, reason, callback,
- (view, event, fromUdfpsView) ->
- onTouch(requestId, event, fromUdfpsView),
- mActivityLaunchAnimator)));
+ mUnlockedScreenOffAnimationController, mHbmProvider, requestId, reason,
+ callback, (view, event, fromUdfpsView) -> onTouch(requestId, event,
+ fromUdfpsView), mActivityLaunchAnimator)));
}
@Override
@@ -281,6 +278,38 @@ public class UdfpsController implements DozeReceiver {
}
/**
+ * Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
+ *
+ * @param sensorId sensor for which the overlay is getting updated.
+ * @param overlayParams See {@link UdfpsOverlayParams}.
+ */
+ public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
+ if (sensorId != mSensorId) {
+ mSensorId = sensorId;
+ Log.w(TAG, "updateUdfpsParams | sensorId has changed");
+ }
+
+ if (!mOverlayParams.equals(overlayParams)) {
+ mOverlayParams = overlayParams;
+
+ final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
+
+ // When the bounds change it's always necessary to re-create the overlay's window with
+ // new LayoutParams. If the overlay needs to be shown, this will re-create and show the
+ // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
+ redrawOverlay();
+ if (wasShowingAltAuth) {
+ mKeyguardViewManager.showGenericBouncer(true);
+ }
+ }
+ }
+
+ // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
+ public void setAuthControllerUpdateUdfpsLocation(@Nullable Runnable r) {
+ mAuthControllerUpdateUdfpsLocation = r;
+ }
+
+ /**
* Calculate the pointer speed given a velocity tracker and the pointer id.
* This assumes that the velocity tracker has already been passed all relevant motion events.
*/
@@ -340,10 +369,11 @@ public class UdfpsController implements DozeReceiver {
}
return !mOverlay.getAnimationViewController().shouldPauseAuth()
- && getSensorLocation().contains(x, y);
+ && mOverlayParams.getSensorBounds().contains((int) x, (int) y);
}
- private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ @VisibleForTesting
+ boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (mOverlay == null) {
Log.w(TAG, "ignoring onTouch with null overlay");
return false;
@@ -438,33 +468,28 @@ public class UdfpsController implements DozeReceiver {
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mAcquiredReceived
&& !exceedsVelocityThreshold) {
- final int rawX = (int) event.getRawX();
- final int rawY = (int) event.getRawY();
- // Default coordinates assume portrait mode.
- int x = rawX;
- int y = rawY;
-
- // Gets the size based on the current rotation of the display.
- Point p = new Point();
- mContext.getDisplay().getRealSize(p);
-
- // Transform x, y to portrait mode if the device is in landscape mode.
- switch (mContext.getDisplay().getRotation()) {
- case Surface.ROTATION_90:
- x = p.y - rawY;
- y = rawX;
- break;
-
- case Surface.ROTATION_270:
- x = rawY;
- y = p.x - rawX;
- break;
-
- default:
- // Do nothing to stay in portrait mode.
+ // Map the touch to portrait mode if the device is in landscape mode.
+ Point portraitTouch = new Point(
+ (int) event.getRawX(idx),
+ (int) event.getRawY(idx)
+ );
+ final int rot = mOverlayParams.getRotation();
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ RotationUtils.rotatePoint(portraitTouch,
+ RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+ mOverlayParams.getLogicalDisplayWidth(),
+ mOverlayParams.getLogicalDisplayHeight()
+ );
}
- onFingerDown(requestId, x, y, minor, major);
+ // Scale the coordinates to native resolution.
+ final float scale = mOverlayParams.getScaleFactor();
+ int scaledX = (int) (portraitTouch.x / scale);
+ int scaledY = (int) (portraitTouch.y / scale);
+ float scaledMinor = minor / scale;
+ float scaledMajor = major / scale;
+
+ onFingerDown(requestId, scaledX, scaledY, scaledMinor, scaledMajor);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = mSystemClock.elapsedRealtime();
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
@@ -571,16 +596,15 @@ public class UdfpsController implements DozeReceiver {
mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = aternateTouchProvider.orElse(null);
- mSensorProps = findFirstUdfps();
- // At least one UDFPS sensor exists
- checkArgument(mSensorProps != null);
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
mainHandler,
- new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps),
+ BiometricDisplayListener.SensorType.UnderDisplayFingerprint.INSTANCE,
() -> {
- onOrientationChanged();
+ if (mAuthControllerUpdateUdfpsLocation != null) {
+ mAuthControllerUpdateUdfpsLocation.run();
+ }
return Unit.INSTANCE;
});
@@ -609,17 +633,6 @@ public class UdfpsController implements DozeReceiver {
}
}
- @Nullable
- private FingerprintSensorPropertiesInternal findFirstUdfps() {
- for (FingerprintSensorPropertiesInternal props :
- mFingerprintManager.getSensorPropertiesInternal()) {
- if (props.isAnyUdfpsType()) {
- return props;
- }
- }
- return null;
- }
-
@Override
public void dozeTimeTick() {
if (mOverlay != null) {
@@ -630,21 +643,6 @@ public class UdfpsController implements DozeReceiver {
}
}
- /**
- * @return where the UDFPS exists on the screen in pixels.
- */
- public RectF getSensorLocation() {
- // This is currently used to calculate the amount of space available for notifications
- // on lockscreen and for the udfps light reveal animation on keyguard.
- // Keyguard is only shown in portrait mode for now, so this will need to
- // be updated if that ever changes.
- final SensorLocationInternal location = mSensorProps.getLocation();
- return new RectF(location.sensorLocationX - location.sensorRadius,
- location.sensorLocationY - location.sensorRadius,
- location.sensorLocationX + location.sensorRadius,
- location.sensorLocationY + location.sensorRadius);
- }
-
private void redrawOverlay() {
UdfpsControllerOverlay overlay = mOverlay;
if (overlay != null) {
@@ -653,26 +651,11 @@ public class UdfpsController implements DozeReceiver {
}
}
- private void onOrientationChanged() {
- // When the configuration changes it's almost always necessary to destroy and re-create
- // the overlay's window to pass it the new LayoutParams.
- // Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
- // of whether it is already hidden.
- final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
-
- // If the overlay needs to be shown, this will re-create and show the overlay with the
- // updated LayoutParams. Otherwise, the overlay will remain hidden.
- redrawOverlay();
- if (wasShowingAltAuth) {
- mKeyguardViewManager.showGenericBouncer(true);
- }
- }
-
private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) {
mExecution.assertIsMainThread();
mOverlay = overlay;
- if (overlay.show(this)) {
+ if (overlay.show(this, mOverlayParams)) {
Log.v(TAG, "showUdfpsOverlay | adding window reason="
+ overlay.getRequestReason());
mOnFingerDown = false;
@@ -814,14 +797,14 @@ public class UdfpsController implements DozeReceiver {
if (mAlternateTouchProvider != null) {
mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
} else {
- mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+ mFingerprintManager.onPointerDown(requestId, mSensorId, x, y, minor, major);
}
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
view.startIllumination(() -> {
- mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
+ mFingerprintManager.onUiReady(requestId, mSensorId);
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
});
}
@@ -839,7 +822,7 @@ public class UdfpsController implements DozeReceiver {
if (mAlternateTouchProvider != null) {
mAlternateTouchProvider.onPointerUp(requestId);
} else {
- mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+ mFingerprintManager.onPointerUp(requestId, mSensorId);
}
for (Callback cb : mCallbacks) {
cb.onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index ee43e932b344..9c8aee4e93a7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -20,17 +20,16 @@ import android.annotation.SuppressLint
import android.annotation.UiThread
import android.content.Context
import android.graphics.PixelFormat
-import android.graphics.Point
+import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
-import android.hardware.biometrics.SensorLocationInternal
import android.hardware.fingerprint.FingerprintManager
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.os.RemoteException
import android.util.Log
+import android.util.RotationUtils
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
@@ -78,7 +77,6 @@ class UdfpsControllerOverlay(
private val systemClock: SystemClock,
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private val sensorProps: FingerprintSensorPropertiesInternal,
private var hbmProvider: UdfpsHbmProvider,
val requestId: Long,
@ShowReason val requestReason: Int,
@@ -90,6 +88,8 @@ class UdfpsControllerOverlay(
var overlayView: UdfpsView? = null
private set
+ private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
+
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
private val coreLayoutParams = WindowManager.LayoutParams(
@@ -101,7 +101,11 @@ class UdfpsControllerOverlay(
fitInsetsTypes = 0
gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ flags =
+ (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ // Avoid announcing window title.
+ accessibilityTitle = " "
}
/** A helper if the [requestReason] was due to enrollment. */
@@ -125,13 +129,14 @@ class UdfpsControllerOverlay(
/** Show the overlay or return false and do nothing if it is already showing. */
@SuppressLint("ClickableViewAccessibility")
- fun show(controller: UdfpsController): Boolean {
+ fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
if (overlayView == null) {
+ overlayParams = params
try {
overlayView = (inflater.inflate(
R.layout.udfps_view, null, false
) as UdfpsView).apply {
- sensorProperties = sensorProps
+ overlayParams = params
setHbmProvider(hbmProvider)
val animation = inflateUdfpsAnimation(this, controller)
if (animation != null) {
@@ -144,8 +149,7 @@ class UdfpsControllerOverlay(
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
- windowManager.addView(this,
- coreLayoutParams.updateForLocation(sensorProps.location, animation))
+ windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
overlayTouchListener = TouchExplorationStateChangeListener {
if (accessibilityManager.isTouchExplorationEnabled) {
@@ -180,13 +184,14 @@ class UdfpsControllerOverlay(
REASON_ENROLL_ENROLLING -> {
UdfpsEnrollViewController(
view.addUdfpsView(R.layout.udfps_enroll_view) {
- updateSensorLocation(sensorProps)
+ updateSensorLocation(overlayParams.sensorBounds)
},
enrollHelper ?: throw IllegalStateException("no enrollment helper"),
statusBarStateController,
panelExpansionStateManager,
dialogManager,
- dumpManager
+ dumpManager,
+ overlayParams.scaleFactor
)
}
BiometricOverlayConstants.REASON_AUTH_KEYGUARD -> {
@@ -280,57 +285,42 @@ class UdfpsControllerOverlay(
/** Checks if the id is relevant for this overlay. */
fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id
- private fun WindowManager.LayoutParams.updateForLocation(
- location: SensorLocationInternal,
+ private fun WindowManager.LayoutParams.updateDimensions(
animation: UdfpsAnimationViewController<*>?
): WindowManager.LayoutParams {
val paddingX = animation?.paddingX ?: 0
val paddingY = animation?.paddingY ?: 0
- flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS
- or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
if (animation != null && animation.listenForTouchesOutsideView()) {
flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
}
- // Default dimensions assume portrait mode.
- x = location.sensorLocationX - location.sensorRadius - paddingX
- y = location.sensorLocationY - location.sensorRadius - paddingY
- height = 2 * location.sensorRadius + 2 * paddingX
- width = 2 * location.sensorRadius + 2 * paddingY
-
- // Gets the size based on the current rotation of the display.
- val p = Point()
- context.display!!.getRealSize(p)
- when (context.display!!.rotation) {
- Surface.ROTATION_90 -> {
- if (!shouldRotate(animation)) {
- Log.v(TAG, "skip rotating udfps location ROTATION_90" +
- " animation=$animation" +
- " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
- " isOccluded=${keyguardStateController.isOccluded}")
- } else {
- Log.v(TAG, "rotate udfps location ROTATION_90")
- x = (location.sensorLocationY - location.sensorRadius - paddingX)
- y = (p.y - location.sensorLocationX - location.sensorRadius - paddingY)
- }
- }
- Surface.ROTATION_270 -> {
- if (!shouldRotate(animation)) {
- Log.v(TAG, "skip rotating udfps location ROTATION_270" +
+ // Original sensorBounds assume portrait mode.
+ val rotatedSensorBounds = Rect(overlayParams.sensorBounds)
+
+ val rot = overlayParams.rotation
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ if (!shouldRotate(animation)) {
+ Log.v(
+ TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
" animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
- " isOccluded=${keyguardStateController.isOccluded}")
- } else {
- Log.v(TAG, "rotate udfps location ROTATION_270")
- x = (p.x - location.sensorLocationY - location.sensorRadius - paddingX)
- y = (location.sensorLocationX - location.sensorRadius - paddingY)
- }
+ " isOccluded=${keyguardStateController.isOccluded}"
+ )
+ } else {
+ Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
+ RotationUtils.rotateBounds(
+ rotatedSensorBounds,
+ overlayParams.naturalDisplayWidth,
+ overlayParams.naturalDisplayHeight,
+ rot
+ )
}
- else -> {}
}
- // avoid announcing window title
- accessibilityTitle = " "
+ x = rotatedSensorBounds.left - paddingX
+ y = rotatedSensorBounds.top - paddingY
+ height = rotatedSensorBounds.height() + 2 * paddingX
+ width = rotatedSensorBounds.width() + 2 * paddingY
return this
}
@@ -363,5 +353,5 @@ private fun Int.isEnrollmentReason() =
@ShowReason
private fun Int.isImportantForAccessibility() =
this == REASON_ENROLL_FIND_SENSOR ||
- this == REASON_ENROLL_ENROLLING ||
- this == BiometricOverlayConstants.REASON_AUTH_BP
+ this == REASON_ENROLL_ENROLLING ||
+ this == BiometricOverlayConstants.REASON_AUTH_BP
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 6afe420a9d02..dbfce2ed2532 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -139,6 +139,9 @@ public class UdfpsDialogMeasureAdapter {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
+ } else if (child.getId() == R.id.description) {
+ //skip description view and compute later
+ continue;
} else {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -150,9 +153,27 @@ public class UdfpsDialogMeasureAdapter {
}
}
+ //re-calculate the height of description
+ View description = mView.findViewById(R.id.description);
+ totalHeight += measureDescription(description, displayHeight, width, totalHeight);
+
return new AuthDialog.LayoutParams(width, totalHeight);
}
+ private int measureDescription(View description, int displayHeight, int currWidth,
+ int currHeight) {
+ //description view should be measured in AuthBiometricFingerprintView#onMeasureInternal
+ //so we could getMeasuredHeight in onMeasureInternalPortrait directly.
+ int newHeight = description.getMeasuredHeight() + currHeight;
+ int limit = (int) (displayHeight * 0.75);
+ if (newHeight > limit) {
+ description.measure(
+ MeasureSpec.makeMeasureSpec(currWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(limit - currHeight, MeasureSpec.EXACTLY));
+ }
+ return description.getMeasuredHeight();
+ }
+
@NonNull
private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height) {
final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 93df2cf0f835..69c37b2b9a62 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -17,7 +17,7 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
@@ -61,13 +61,11 @@ public class UdfpsEnrollView extends UdfpsAnimationView {
return mFingerprintDrawable;
}
- void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
+ void updateSensorLocation(@NonNull Rect sensorBounds) {
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
- final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
- final int sensorWidth = sensorHeight;
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
- params.width = sensorWidth;
- params.height = sensorHeight;
+ params.width = sensorBounds.width();
+ params.height = sensorBounds.height();
fingerprintAccessibilityView.setLayoutParams(params);
fingerprintAccessibilityView.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 2ca103bf942f..2ed60e555c58 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -56,11 +56,12 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
@NonNull StatusBarStateController statusBarStateController,
@NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull SystemUIDialogManager systemUIDialogManager,
- @NonNull DumpManager dumpManager) {
+ @NonNull DumpManager dumpManager,
+ float scaleFactor) {
super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
dumpManager);
- mEnrollProgressBarRadius = getContext().getResources()
- .getInteger(R.integer.config_udfpsEnrollProgressBar);
+ mEnrollProgressBarRadius = (int) (scaleFactor * getContext().getResources().getInteger(
+ R.integer.config_udfpsEnrollProgressBar));
mEnrollHelper = enrollHelper;
mView.setEnrollHelper(mEnrollHelper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
new file mode 100644
index 000000000000..d725dfbfe216
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
@@ -0,0 +1,43 @@
+package com.android.systemui.biometrics
+
+import android.graphics.Rect
+import android.view.Surface
+import android.view.Surface.Rotation
+
+/**
+ * Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
+ *
+ * @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
+ * in pixels, for the current resolution.
+ * @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
+ * for the current resolution.
+ * @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
+ * for the current resolution.
+ * @property scaleFactor ratio of a dimension in the current resolution to the corresponding
+ * dimension in the native resolution.
+ * @property rotation current rotation of the display.
+ */
+
+data class UdfpsOverlayParams(
+ val sensorBounds: Rect = Rect(),
+ val naturalDisplayWidth: Int = 0,
+ val naturalDisplayHeight: Int = 0,
+ val scaleFactor: Float = 1f,
+ @Rotation val rotation: Int = Surface.ROTATION_0
+) {
+ /** See [android.view.DisplayInfo.logicalWidth] */
+ val logicalDisplayWidth
+ get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ naturalDisplayHeight
+ } else {
+ naturalDisplayWidth
+ }
+
+ /** See [android.view.DisplayInfo.logicalHeight] */
+ val logicalDisplayHeight
+ get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ naturalDisplayWidth
+ } else {
+ naturalDisplayHeight
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 75e6aa032343..2aa345ab28dc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -21,7 +21,6 @@ import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.graphics.RectF
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
@@ -39,6 +38,8 @@ class UdfpsView(
attrs: AttributeSet?
) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
+ // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
+ // overlayParams.sensorBounds
private val sensorRect = RectF()
private var hbmProvider: UdfpsHbmProvider? = null
private val debugTextPaint = Paint().apply {
@@ -62,8 +63,8 @@ class UdfpsView(
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null
- /** Properties used to obtain the sensor location. */
- var sensorProperties: FingerprintSensorPropertiesInternal? = null
+ /** Parameters that affect the position and size of the overlay. Visible for testing. */
+ var overlayParams = UdfpsOverlayParams()
/** Debug message. */
var debugMessage: String? = null
@@ -94,13 +95,12 @@ class UdfpsView(
val paddingX = animationViewController?.paddingX ?: 0
val paddingY = animationViewController?.paddingY ?: 0
- val sensorRadius = sensorProperties?.location?.sensorRadius ?: 0
sensorRect.set(
paddingX.toFloat(),
paddingY.toFloat(),
- (2 * sensorRadius + paddingX).toFloat(),
- (2 * sensorRadius + paddingY).toFloat()
+ (overlayParams.sensorBounds.width() + paddingX).toFloat(),
+ (overlayParams.sensorBounds.height() + paddingY).toFloat()
)
animationViewController?.onSensorRectUpdated(RectF(sensorRect))
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index b7c4009a5aed..eef9d5bebd75 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -137,7 +137,6 @@ public class ClipboardOverlayController {
private Runnable mOnSessionCompleteListener;
-
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -145,6 +144,7 @@ public class ClipboardOverlayController {
private BroadcastReceiver mScreenshotReceiver;
private boolean mBlockAttach = false;
+ private Animator mExitAnimator;
public ClipboardOverlayController(Context context,
BroadcastDispatcher broadcastDispatcher,
@@ -200,6 +200,7 @@ public class ClipboardOverlayController {
@Override
public void onSwipeDismissInitiated(Animator animator) {
mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+ mExitAnimator = animator;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -233,7 +234,6 @@ public class ClipboardOverlayController {
mWindow.setContentView(mContainer);
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
mView.requestLayout();
- mView.post(this::animateIn);
});
mTimeoutHandler.setOnTimeoutRunnable(() -> {
@@ -273,6 +273,9 @@ public class ClipboardOverlayController {
}
void setClipData(ClipData clipData, String clipSource) {
+ if (mExitAnimator != null && mExitAnimator.isRunning()) {
+ mExitAnimator.cancel();
+ }
reset();
if (clipData == null || clipData.getItemCount() == 0) {
showTextPreview(mContext.getResources().getString(
@@ -305,6 +308,7 @@ public class ClipboardOverlayController {
} else {
mRemoteCopyChip.setVisibility(View.GONE);
}
+ withWindowAttached(() -> mContainer.post(this::animateIn));
mTimeoutHandler.resetTimeout();
}
@@ -411,7 +415,7 @@ public class ClipboardOverlayController {
private void showTextPreview(CharSequence text) {
mTextPreview.setVisibility(View.VISIBLE);
mImagePreview.setVisibility(View.GONE);
- mTextPreview.setText(text);
+ mTextPreview.setText(text.subSequence(0, Math.min(500, text.length())));
mEditChip.setVisibility(View.GONE);
}
@@ -428,10 +432,6 @@ public class ClipboardOverlayController {
}
private void showEditableImage(Uri uri) {
- mTextPreview.setVisibility(View.GONE);
- mImagePreview.setVisibility(View.VISIBLE);
- mEditChip.setAlpha(1f);
- mActionContainerBackground.setVisibility(View.VISIBLE);
ContentResolver resolver = mContext.getContentResolver();
try {
int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
@@ -441,7 +441,14 @@ public class ClipboardOverlayController {
mImagePreview.setImageBitmap(thumbnail);
} catch (IOException e) {
Log.e(TAG, "Thumbnail loading failed", e);
+ showTextPreview(
+ mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
+ return;
}
+ mTextPreview.setVisibility(View.GONE);
+ mImagePreview.setVisibility(View.VISIBLE);
+ mEditChip.setAlpha(1f);
+ mActionContainerBackground.setVisibility(View.VISIBLE);
View.OnClickListener listener = v -> editImage(uri);
mEditChip.setOnClickListener(listener);
mEditChip.setContentDescription(
@@ -472,12 +479,23 @@ public class ClipboardOverlayController {
private void animateOut() {
Animator anim = getExitAnimation();
anim.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mCancelled = true;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- hideImmediate();
+ if (!mCancelled) {
+ hideImmediate();
+ }
}
});
+ mExitAnimator = anim;
anim.start();
}
@@ -630,6 +648,7 @@ public class ClipboardOverlayController {
private void reset() {
mView.setTranslationX(0);
mContainer.setAlpha(0);
+ mActionContainerBackground.setVisibility(View.GONE);
resetActionChips();
mTimeoutHandler.cancelTimeout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/AndroidInternalsModule.java b/packages/SystemUI/src/com/android/systemui/dagger/AndroidInternalsModule.java
index 48c54bca69ee..09928829b195 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/AndroidInternalsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/AndroidInternalsModule.java
@@ -19,9 +19,13 @@ package com.android.systemui.dagger;
import android.content.Context;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
+import javax.inject.Singleton;
+
import dagger.Module;
import dagger.Provides;
@@ -32,14 +36,14 @@ import dagger.Provides;
public class AndroidInternalsModule {
/** */
@Provides
- @SysUISingleton
+ @Singleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}
/** */
@Provides
- @SysUISingleton
+ @Singleton
public MetricsLogger provideMetricsLogger() {
return new MetricsLogger();
}
@@ -50,4 +54,10 @@ public class AndroidInternalsModule {
return new NotificationMessagingUtil(context);
}
+ /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
+ @Provides
+ @Singleton
+ static UiEventLogger provideUiEventLogger() {
+ return new UiEventLoggerImpl();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 31ad36fb21d8..19e98f26cacb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -27,7 +27,6 @@ import dagger.Module;
*/
@Deprecated
@Module(includes = {
- AndroidInternalsModule.class,
BroadcastDispatcherModule.class,
LeakModule.class,
NightDisplayListenerModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index e512b7cf3a20..5b6ddd8471da 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -90,6 +90,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.systemui.Prefs;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.Optional;
@@ -445,6 +446,13 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ @TestHarness
+ static boolean provideIsTestHarness() {
+ return ActivityManager.isRunningInUserTestHarness();
+ }
+
+ @Provides
+ @Singleton
static TrustManager provideTrustManager(Context context) {
return context.getSystemService(TrustManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index 620feec8fbb8..ca725c0e39ff 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -16,23 +16,15 @@
package com.android.systemui.dagger;
-import android.app.ActivityManager;
import android.content.Context;
import android.util.DisplayMetrics;
+import android.view.Display;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.dagger.qualifiers.TestHarness;
-import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.plugins.PluginsModule;
import com.android.systemui.unfold.UnfoldTransitionModule;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-import javax.inject.Singleton;
-
import dagger.Module;
import dagger.Provides;
@@ -52,43 +44,29 @@ import dagger.Provides;
* Please use discretion when adding things to the global scope.
*/
@Module(includes = {
+ AndroidInternalsModule.class,
FrameworkServicesModule.class,
GlobalConcurrencyModule.class,
UnfoldTransitionModule.class,
PluginsModule.class,
})
public class GlobalModule {
-
- /** */
- @Provides
- public DisplayMetrics provideDisplayMetrics(Context context) {
- DisplayMetrics displayMetrics = new DisplayMetrics();
- context.getDisplay().getMetrics(displayMetrics);
- return displayMetrics;
- }
-
- /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
- @Provides
- @Singleton
- static UiEventLogger provideUiEventLogger() {
- return new UiEventLoggerImpl();
- }
-
+ /**
+ * TODO(b/229228871): This should be the default. No undecorated context should be available.
+ */
@Provides
- @TestHarness
- static boolean provideIsTestHarness() {
- return ActivityManager.isRunningInUserTestHarness();
+ @Application
+ public Context provideApplicationContext(Context context) {
+ return context.getApplicationContext();
}
/**
- * Provide an Executor specifically for running UI operations on a separate thread.
- *
- * Keep submitted runnables short and to the point, just as with any other UI code.
+ * @deprecated Deprecdated because {@link Display#getMetrics} is deprecated.
*/
@Provides
- @Singleton
- @UiBackground
- public static Executor provideUiBackgroundExecutor() {
- return Executors.newSingleThreadExecutor();
+ public DisplayMetrics provideDisplayMetrics(Context context) {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ context.getDisplay().getMetrics(displayMetrics);
+ return displayMetrics;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Application.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Application.java
new file mode 100644
index 000000000000..21e53a5b51d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Application.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * Used to qualify a context as {@link Context#getApplicationContext}
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Application {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index 3731eca4ee86..b5a0cfcb1c18 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -68,6 +68,15 @@ class RoundedCornerResDelegate(
reloadMeasures()
}
+ var physicalPixelDisplaySizeRatio: Float = 1f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ reloadMeasures()
+ }
+
init {
reloadRes()
reloadMeasures()
@@ -134,6 +143,19 @@ class RoundedCornerResDelegate(
bottomRoundedSize = Size(length, length)
}
}
+
+ if (physicalPixelDisplaySizeRatio != 1f) {
+ if (topRoundedSize.width != 0) {
+ topRoundedSize = Size(
+ (physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(),
+ (physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt())
+ }
+ if (bottomRoundedSize.width != 0) {
+ bottomRoundedSize = Size(
+ (physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(),
+ (physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt())
+ }
+ }
}
private fun getDrawable(
@@ -160,5 +182,6 @@ class RoundedCornerResDelegate(
pw.println(" topRoundedSize(w,h)=(${topRoundedSize.width},${topRoundedSize.height})")
pw.println(" bottomRoundedSize(w,h)=(${bottomRoundedSize.width}," +
"${bottomRoundedSize.height})")
+ pw.println(" physicalPixelDisplaySizeRatio=$physicalPixelDisplaySizeRatio")
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index db225cf498b0..96f77b3654c5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -38,7 +38,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.Complication;
-import com.android.systemui.dreams.complication.DreamPreviewComplication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
@@ -63,7 +62,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final DreamPreviewComplication mPreviewComplication;
private final UiEventLogger mUiEventLogger;
// A reference to the {@link Window} used to hold the dream overlay.
@@ -127,14 +125,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DreamPreviewComplication previewComplication,
UiEventLogger uiEventLogger) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController;
- mPreviewComplication = previewComplication;
mUiEventLogger = uiEventLogger;
final DreamOverlayComponent component =
@@ -159,9 +155,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
windowManager.removeView(mWindow.getDecorView());
}
mStateController.setOverlayActive(false);
- mPreviewComplication.setDreamLabel(null);
- mStateController.removeComplication(mPreviewComplication);
- mStateController.setPreviewMode(false);
mDestroyed = true;
super.onDestroy();
}
@@ -177,11 +170,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
return;
}
mStateController.setShouldShowComplications(shouldShowComplications());
- mStateController.setPreviewMode(isPreviewMode());
- if (isPreviewMode()) {
- mPreviewComplication.setDreamLabel(getDreamLabel());
- mStateController.addComplication(mPreviewComplication);
- }
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
mStateController.setOverlayActive(true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index 6860998680cb..fc71e2fb2329 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -50,7 +50,6 @@ public class DreamOverlayStateController implements
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
- public static final int STATE_PREVIEW_MODE = 1 << 1;
private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2;
@@ -250,18 +249,4 @@ public class DreamOverlayStateController implements
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
}
-
- /**
- * Sets whether the dream is running in preview mode.
- */
- public void setPreviewMode(boolean isPreviewMode) {
- modifyState(isPreviewMode ? OP_SET_STATE : OP_CLEAR_STATE, STATE_PREVIEW_MODE);
- }
-
- /**
- * Returns whether the dream is running in preview mode.
- */
- public boolean isPreviewMode() {
- return containsState(STATE_PREVIEW_MODE);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
deleted file mode 100644
index cc2e57128204..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
+++ /dev/null
@@ -1,137 +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.dreams.complication;
-
-import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DREAM_LABEL;
-import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS;
-import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_VIEW;
-
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent;
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * Preview complication shown when user is previewing a dream.
- */
-public class DreamPreviewComplication implements Complication {
- DreamPreviewComplicationComponent.Factory mComponentFactory;
- @Nullable
- private CharSequence mDreamLabel;
-
- /**
- * Default constructor for {@link DreamPreviewComplication}.
- */
- @Inject
- public DreamPreviewComplication(
- DreamPreviewComplicationComponent.Factory componentFactory) {
- mComponentFactory = componentFactory;
- }
-
- /**
- * Create {@link DreamPreviewViewHolder}.
- */
- @Override
- public ViewHolder createView(ComplicationViewModel model) {
- return mComponentFactory.create(model, mDreamLabel).getViewHolder();
- }
-
- /**
- * Sets the user-facing label for the current dream.
- */
- public void setDreamLabel(@Nullable CharSequence dreamLabel) {
- mDreamLabel = dreamLabel;
- }
-
- /**
- * ViewHolder to contain value/logic associated with a Preview Complication View.
- */
- public static class DreamPreviewViewHolder implements ViewHolder {
- private final TextView mView;
- private final ComplicationLayoutParams mLayoutParams;
- private final DreamPreviewViewController mViewController;
-
- @Inject
- DreamPreviewViewHolder(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view,
- DreamPreviewViewController controller,
- @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS)
- ComplicationLayoutParams layoutParams,
- @Named(DREAM_LABEL) @Nullable CharSequence dreamLabel) {
- mView = view;
- mLayoutParams = layoutParams;
- mViewController = controller;
- mViewController.init();
-
- if (!TextUtils.isEmpty(dreamLabel)) {
- mView.setText(dreamLabel);
- }
- for (Drawable drawable : mView.getCompoundDrawablesRelative()) {
- if (drawable instanceof BitmapDrawable) {
- drawable.setAutoMirrored(true);
- }
- }
- }
-
- @Override
- public View getView() {
- return mView;
- }
-
- @Override
- public ComplicationLayoutParams getLayoutParams() {
- return mLayoutParams;
- }
-
- @Override
- public int getCategory() {
- return CATEGORY_SYSTEM;
- }
- }
-
- /**
- * ViewController to contain value/logic associated with a Preview Complication View.
- */
- static class DreamPreviewViewController extends ViewController<TextView> {
- private final ComplicationViewModel mViewModel;
-
- @Inject
- DreamPreviewViewController(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view,
- ComplicationViewModel viewModel) {
- super(view);
- mViewModel = viewModel;
- }
-
- @Override
- protected void onViewAttached() {
- mView.setOnClickListener(v -> mViewModel.exitDream());
- }
-
- @Override
- protected void onViewDetached() {
- mView.setOnClickListener(null);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java
deleted file mode 100644
index 502e31ed0c7f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java
+++ /dev/null
@@ -1,117 +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.dreams.complication.dagger;
-
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
-import com.android.systemui.dreams.complication.ComplicationLayoutParams;
-import com.android.systemui.dreams.complication.ComplicationViewModel;
-import com.android.systemui.dreams.complication.DreamPreviewComplication.DreamPreviewViewHolder;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Named;
-import javax.inject.Scope;
-
-import dagger.BindsInstance;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-
-/**
- * {@link DreamPreviewComplicationComponent} is responsible for generating dependencies
- * surrounding the
- * Preview {@link com.android.systemui.dreams.complication.Complication}, such as the layout
- * details.
- */
-@Subcomponent(modules = {
- DreamPreviewComplicationComponent.DreamPreviewComplicationModule.class,
-})
-@DreamPreviewComplicationComponent.DreamPreviewComplicationScope
-public interface DreamPreviewComplicationComponent {
- String DREAM_LABEL = "dream_label";
-
- /**
- * Creates {@link DreamPreviewViewHolder}.
- */
- DreamPreviewViewHolder getViewHolder();
-
- @Documented
- @Retention(RUNTIME)
- @Scope
- @interface DreamPreviewComplicationScope {
- }
-
- /**
- * Generates {@link DreamPreviewComplicationComponent}.
- */
- @Subcomponent.Factory
- interface Factory {
- DreamPreviewComplicationComponent create(
- @BindsInstance ComplicationViewModel viewModel,
- @Named(DREAM_LABEL) @BindsInstance @Nullable CharSequence dreamLabel);
- }
-
- /**
- * Scoped values for {@link DreamPreviewComplicationComponent}.
- */
- @Module
- interface DreamPreviewComplicationModule {
- String DREAM_PREVIEW_COMPLICATION_VIEW = "preview_complication_view";
- String DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS = "preview_complication_layout_params";
- // Order weight of insert into parent container
- int INSERT_ORDER_WEIGHT = 1000;
-
- /**
- * Provides the complication view.
- */
- @Provides
- @DreamPreviewComplicationScope
- @Named(DREAM_PREVIEW_COMPLICATION_VIEW)
- static TextView provideComplicationView(LayoutInflater layoutInflater) {
- return Preconditions.checkNotNull((TextView)
- layoutInflater.inflate(R.layout.dream_overlay_complication_preview,
- null, false),
- "R.layout.dream_overlay_complication_preview did not properly inflated");
- }
-
- /**
- * Provides the layout parameters for the complication view.
- */
- @Provides
- @DreamPreviewComplicationScope
- @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideLayoutParams() {
- return new ComplicationLayoutParams(0,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_TOP
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index c7b02cd00e96..c1dff248818f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -19,7 +19,6 @@ package com.android.systemui.dreams.dagger;
import android.content.Context;
import com.android.settingslib.dream.DreamBackend;
-import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import dagger.Module;
@@ -33,7 +32,6 @@ import dagger.Provides;
},
subcomponents = {
DreamOverlayComponent.class,
- DreamPreviewComplicationComponent.class,
})
public interface DreamModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index afa7d5e0a9c4..e963c39329ca 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -151,7 +151,7 @@ public class Flags {
/***************************************/
// 900 - media
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
- public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
+ public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0f5022df17d8..d6843bf5f58b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -119,6 +119,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -2784,6 +2785,13 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onWakeAndUnlocking() {
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
+
+ // We're going to animate in the Launcher, so ask WM to clear the task snapshot so we don't
+ // initially display an old snapshot with all of the icons visible. We're System UI, so
+ // we're allowed to pass in null to ask WM to find the home activity for us to prevent
+ // needing to IPC to Launcher.
+ ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(null /* homeActivity */);
+
keyguardDone();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 79ac9e8b0986..39f00f45aac2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -176,7 +176,6 @@ public class LogModule {
return factory.create("MediaTttReceiver", 20);
}
-
/**
* Provides a logging buffer for logs related to the media mute-await connections. See
* {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
@@ -199,6 +198,16 @@ public class LogModule {
return factory.create("NearbyMediaDevicesLog", 20);
}
+ /**
+ * Provides a buffer for logs related to media view events
+ */
+ @Provides
+ @SysUISingleton
+ @MediaViewLog
+ public static LogBuffer provideMediaViewLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaView", 100);
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
new file mode 100644
index 000000000000..75a34fc22c3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link LogBuffer} for {@link com.android.systemui.media.MediaViewLogger}
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface MediaViewLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index 2ae1806f3b3d..5a214d1cd5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -95,66 +95,61 @@ class ColorSchemeTransition internal constructor(
val surfaceColor = colorTransitionFactory(
bgColor,
- { colorScheme -> colorScheme.accent2[9] }, // A2-800
- { surfaceColor ->
- val colorList = ColorStateList.valueOf(surfaceColor)
- mediaViewHolder.player.backgroundTintList = colorList
- mediaViewHolder.albumView.foregroundTintList = colorList
- mediaViewHolder.albumView.backgroundTintList = colorList
- mediaViewHolder.seamlessIcon.imageTintList = colorList
- mediaViewHolder.seamlessText.setTextColor(surfaceColor)
- mediaViewHolder.dismissText.setTextColor(surfaceColor)
- })
+ ::surfaceFromScheme
+ ) { surfaceColor ->
+ val colorList = ColorStateList.valueOf(surfaceColor)
+ mediaViewHolder.player.backgroundTintList = colorList
+ mediaViewHolder.albumView.foregroundTintList = colorList
+ mediaViewHolder.albumView.backgroundTintList = colorList
+ mediaViewHolder.seamlessIcon.imageTintList = colorList
+ mediaViewHolder.seamlessText.setTextColor(surfaceColor)
+ mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
+ }
val accentPrimary = colorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
- { colorScheme -> colorScheme.accent1[2] }, // A1-100
- { accentPrimary ->
- val accentColorList = ColorStateList.valueOf(accentPrimary)
- mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
- mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
- mediaViewHolder.settings.imageTintList = accentColorList
- mediaViewHolder.cancelText.backgroundTintList = accentColorList
- mediaViewHolder.dismissText.backgroundTintList = accentColorList
- })
+ ::accentPrimaryFromScheme
+ ) { accentPrimary ->
+ val accentColorList = ColorStateList.valueOf(accentPrimary)
+ mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
+ mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
+ mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
+ }
val textPrimary = colorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
- { colorScheme -> colorScheme.neutral1[1] }, // N1-50
- { textPrimary ->
- mediaViewHolder.titleText.setTextColor(textPrimary)
- val textColorList = ColorStateList.valueOf(textPrimary)
- mediaViewHolder.seekBar.thumb.setTintList(textColorList)
- mediaViewHolder.seekBar.progressTintList = textColorList
- mediaViewHolder.longPressText.setTextColor(textColorList)
- mediaViewHolder.cancelText.setTextColor(textColorList)
- mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList)
- mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList)
- for (button in mediaViewHolder.getTransparentActionButtons()) {
- button.imageTintList = textColorList
- }
- })
+ ::textPrimaryFromScheme
+ ) { textPrimary ->
+ mediaViewHolder.titleText.setTextColor(textPrimary)
+ val textColorList = ColorStateList.valueOf(textPrimary)
+ mediaViewHolder.seekBar.thumb.setTintList(textColorList)
+ mediaViewHolder.seekBar.progressTintList = textColorList
+ mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList)
+ mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList)
+ for (button in mediaViewHolder.getTransparentActionButtons()) {
+ button.imageTintList = textColorList
+ }
+ mediaViewHolder.gutsViewHolder.setTextPrimaryColor(textPrimary)
+ }
val textPrimaryInverse = colorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimaryInverse),
- { colorScheme -> colorScheme.neutral1[10] }, // N1-900
- { textPrimaryInverse ->
- mediaViewHolder.actionPlayPause.imageTintList =
- ColorStateList.valueOf(textPrimaryInverse)
- })
+ ::textPrimaryInverseFromScheme
+ ) { textPrimaryInverse ->
+ mediaViewHolder.actionPlayPause.imageTintList = ColorStateList.valueOf(textPrimaryInverse)
+ }
val textSecondary = colorTransitionFactory(
loadDefaultColor(R.attr.textColorSecondary),
- { colorScheme -> colorScheme.neutral2[3] }, // N2-200
- { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) })
+ ::textSecondaryFromScheme
+ ) { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) }
val textTertiary = colorTransitionFactory(
loadDefaultColor(R.attr.textColorTertiary),
- { colorScheme -> colorScheme.neutral2[5] }, // N2-400
- { textTertiary ->
- mediaViewHolder.seekBar.progressBackgroundTintList =
- ColorStateList.valueOf(textTertiary)
- })
+ ::textTertiaryFromScheme
+ ) { textTertiary ->
+ mediaViewHolder.seekBar.progressBackgroundTintList = ColorStateList.valueOf(textTertiary)
+ }
val colorTransitions = arrayOf(
surfaceColor, accentPrimary, textPrimary,
@@ -167,4 +162,4 @@ class ColorSchemeTransition internal constructor(
fun updateColorScheme(colorScheme: ColorScheme?) {
colorTransitions.forEach { it.updateColorScheme(colorScheme) }
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt
new file mode 100644
index 000000000000..1a48a84aff23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/GutsViewHolder.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.content.res.ColorStateList
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.monet.ColorScheme
+
+/**
+ * A view holder for the guts menu of a media player. The guts are shown when the user long-presses
+ * on the media player.
+ *
+ * Both [MediaViewHolder] and [RecommendationViewHolder] use the same guts menu layout, so this
+ * class helps share logic between the two.
+ */
+class GutsViewHolder constructor(itemView: View) {
+ val gutsText: TextView = itemView.requireViewById(R.id.remove_text)
+ val cancel: View = itemView.requireViewById(R.id.cancel)
+ val cancelText: TextView = itemView.requireViewById(R.id.cancel_text)
+ val dismiss: ViewGroup = itemView.requireViewById(R.id.dismiss)
+ val dismissText: TextView = itemView.requireViewById(R.id.dismiss_text)
+ val settings: ImageButton = itemView.requireViewById(R.id.settings)
+
+ /** Marquees the main text of the guts menu. */
+ fun marquee(start: Boolean, delay: Long, tag: String) {
+ val gutsTextHandler = gutsText.handler
+ if (gutsTextHandler == null) {
+ Log.d(tag, "marquee while longPressText.getHandler() is null", Exception())
+ return
+ }
+ gutsTextHandler.postDelayed( { gutsText.isSelected = start }, delay)
+ }
+
+ /** Sets the right colors on all the guts views based on the given [ColorScheme]. */
+ fun setColors(colorScheme: ColorScheme) {
+ setSurfaceColor(surfaceFromScheme(colorScheme))
+ setTextPrimaryColor(textPrimaryFromScheme(colorScheme))
+ setAccentPrimaryColor(accentPrimaryFromScheme(colorScheme))
+ }
+
+ /** Sets the surface color on all guts views that use it. */
+ fun setSurfaceColor(surfaceColor: Int) {
+ dismissText.setTextColor(surfaceColor)
+ }
+
+ /** Sets the primary accent color on all guts views that use it. */
+ fun setAccentPrimaryColor(accentPrimary: Int) {
+ val accentColorList = ColorStateList.valueOf(accentPrimary)
+ settings.imageTintList = accentColorList
+ cancelText.backgroundTintList = accentColorList
+ dismissText.backgroundTintList = accentColorList
+ }
+
+ /** Sets the primary text color on all guts views that use it. */
+ fun setTextPrimaryColor(textPrimary: Int) {
+ val textColorList = ColorStateList.valueOf(textPrimary)
+ gutsText.setTextColor(textColorList)
+ cancelText.setTextColor(textColorList)
+ }
+
+ companion object {
+ val ids = setOf(
+ R.id.remove_text,
+ R.id.cancel,
+ R.id.dismiss,
+ R.id.settings
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ca25a18503ec..7b72ab732543 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -533,7 +533,9 @@ class MediaCarouselController @Inject constructor(
}
private fun recreatePlayers() {
- pageIndicator.tintList = ColorStateList.valueOf(R.color.material_dynamic_neutral_variant80)
+ pageIndicator.tintList = ColorStateList.valueOf(
+ context.getColor(R.color.media_paging_indicator)
+ )
MediaPlayerData.mediaData().forEach { (key, data, isSsMediaRec) ->
if (isSsMediaRec) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt
new file mode 100644
index 000000000000..97c6014c91bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import com.android.systemui.monet.ColorScheme
+
+/** Returns the surface color for media controls based on the scheme. */
+internal fun surfaceFromScheme(scheme: ColorScheme) = scheme.accent2[9] // A2-800
+
+/** Returns the primary accent color for media controls based on the scheme. */
+internal fun accentPrimaryFromScheme(scheme: ColorScheme) = scheme.accent1[2] // A1-100
+
+/** Returns the primary text color for media controls based on the scheme. */
+internal fun textPrimaryFromScheme(scheme: ColorScheme) = scheme.neutral1[1] // N1-50
+
+/** Returns the inverse of the primary text color for media controls based on the scheme. */
+internal fun textPrimaryInverseFromScheme(scheme: ColorScheme) = scheme.neutral1[10] // N1-900
+
+/** Returns the secondary text color for media controls based on the scheme. */
+internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2[3] // N2-200
+
+/** Returns the tertiary text color for media controls based on the scheme. */
+internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2[5] // N2-400
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 48a63ed0a2d6..af54e966ed9c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -60,7 +60,6 @@ import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
-import com.android.settingslib.Utils;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.R;
@@ -176,9 +175,12 @@ public class MediaControlPanel {
private String mPackageName;
private boolean mIsScrubbing = false;
+ private boolean mIsSeekBarEnabled = false;
private final SeekBarViewModel.ScrubbingChangeListener mScrubbingChangeListener =
this::setIsScrubbing;
+ private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
+ this::setIsSeekBarEnabled;
/**
* Initialize a new control panel
@@ -235,8 +237,9 @@ public class MediaControlPanel {
public void onDestroy() {
if (mSeekBarObserver != null) {
mSeekBarViewModel.getProgress().removeObserver(mSeekBarObserver);
- mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
}
+ mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
+ mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
mSeekBarViewModel.onDestroy();
mMediaViewController.onDestroy();
}
@@ -283,7 +286,7 @@ public class MediaControlPanel {
}
/** Sets whether the user is touching the seek bar to change the track position. */
- public void setIsScrubbing(boolean isScrubbing) {
+ private void setIsScrubbing(boolean isScrubbing) {
if (mMediaData == null || mMediaData.getSemanticActions() == null) {
return;
}
@@ -295,6 +298,14 @@ public class MediaControlPanel {
updateDisplayForScrubbingChange(mMediaData.getSemanticActions()));
}
+ private void setIsSeekBarEnabled(boolean isSeekBarEnabled) {
+ if (isSeekBarEnabled == this.mIsSeekBarEnabled) {
+ return;
+ }
+ this.mIsSeekBarEnabled = isSeekBarEnabled;
+ updateSeekBarVisibility();
+ }
+
/**
* Get the context
*
@@ -313,6 +324,7 @@ public class MediaControlPanel {
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
+ mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
vh.getPlayer().setOnLongClickListener(v -> {
@@ -324,17 +336,6 @@ public class MediaControlPanel {
return true;
}
});
- vh.getCancel().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts();
- }
- });
- vh.getSettings().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
- }
- });
TextView titleText = mMediaViewHolder.getTitleText();
TextView artistText = mMediaViewHolder.getArtistText();
@@ -379,17 +380,6 @@ public class MediaControlPanel {
return true;
}
});
- mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts();
- }
- });
- mRecommendationViewHolder.getSettings().setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
- }
- });
}
/** Bind this player view based on the data given. */
@@ -449,9 +439,9 @@ public class MediaControlPanel {
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
bindOutputSwitcherChip(data);
- bindLongPressMenu(data);
- bindActionButtons(data);
+ bindGutsMenuForPlayer(data);
bindScrubbingTime(data);
+ bindActionButtons(data);
boolean isSongUpdated = bindSongMetadata(data);
bindArtworkAndColors(data, isSongUpdated);
@@ -519,24 +509,8 @@ public class MediaControlPanel {
});
}
- private void bindLongPressMenu(MediaData data) {
- boolean isDismissible = data.isClearable();
- String dismissText;
- if (isDismissible) {
- dismissText = mContext.getString(R.string.controls_media_close_session, data.getApp());
- } else {
- dismissText = mContext.getString(R.string.controls_media_active_session);
- }
- mMediaViewHolder.getLongPressText().setText(dismissText);
-
- // Dismiss button
- mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
- mMediaViewHolder.getDismiss().setEnabled(isDismissible);
- mMediaViewHolder.getDismiss().setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
- logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT);
- mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
-
+ private void bindGutsMenuForPlayer(MediaData data) {
+ Runnable onDismissClickedRunnable = () -> {
if (mKey != null) {
closeGuts();
if (!mMediaDataManagerLazy.get().dismissMediaData(mKey,
@@ -549,7 +523,13 @@ public class MediaControlPanel {
Log.w(TAG, "Dismiss media with null notification. Token uid="
+ data.getToken().getUid());
}
- });
+ };
+
+ bindGutsMenuCommon(
+ /* isDismissible= */ data.isClearable(),
+ data.getApp(),
+ mMediaViewHolder.getGutsViewHolder(),
+ onDismissClickedRunnable);
}
private boolean bindSongMetadata(MediaData data) {
@@ -735,13 +715,18 @@ public class MediaControlPanel {
/* showInCompact= */ false);
}
}
+
+ updateSeekBarVisibility();
+ }
+
+ private void updateSeekBarVisibility() {
+ ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
expandedSet.setVisibility(R.id.media_progress_bar, getSeekBarVisibility());
- expandedSet.setAlpha(R.id.media_progress_bar, mSeekBarViewModel.getEnabled() ? 1.0f : 0.0f);
+ expandedSet.setAlpha(R.id.media_progress_bar, mIsSeekBarEnabled ? 1.0f : 0.0f);
}
private int getSeekBarVisibility() {
- boolean seekbarEnabled = mSeekBarViewModel.getEnabled();
- if (seekbarEnabled) {
+ if (mIsSeekBarEnabled) {
return ConstraintSet.VISIBLE;
}
// If disabled and "neighbours" are visible, set progress bar to INVISIBLE instead of GONE
@@ -751,8 +736,7 @@ public class MediaControlPanel {
private boolean areAnyExpandedBottomActionsVisible() {
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- int[] referencedIds = mMediaViewHolder.getActionsTopBarrier().getReferencedIds();
- for (int id : referencedIds) {
+ for (int id : MediaViewHolder.Companion.getExpandedBottomActionIds()) {
if (expandedSet.getVisibility(id) == ConstraintSet.VISIBLE) {
return true;
}
@@ -872,7 +856,6 @@ public class MediaControlPanel {
}
/** Updates all the views that might change due to a scrubbing state change. */
- // TODO(b/209656742): Handle scenarios where actionPrev and/or actionNext aren't active.
private void updateDisplayForScrubbingChange(@NonNull MediaButton semanticActions) {
// Update visibilities of the scrubbing time views and the scrubbing-dependent buttons.
bindScrubbingTime(mMediaData);
@@ -957,8 +940,6 @@ public class MediaControlPanel {
mPackageName = data.getPackageName();
mInstanceId = data.getInstanceId();
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
- recommendationCard.setBackgroundTintList(
- Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
List<SmartspaceAction> mediaRecommendationList = data.getRecommendations();
if (mediaRecommendationList == null || mediaRecommendationList.isEmpty()) {
@@ -982,6 +963,7 @@ public class MediaControlPanel {
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
headerLogoImageView.setImageDrawable(icon);
+ fetchAndUpdateRecommendationColors(icon);
// Set up media source app's label text.
CharSequence appName = getAppName(data.getCardAction());
@@ -1057,8 +1039,6 @@ public class MediaControlPanel {
TextView titleView =
mRecommendationViewHolder.getMediaTitles().get(uiComponentIndex);
titleView.setText(title);
- titleView.setTextColor(Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.textColorPrimary));
// TODO(b/223603970): If none of them have titles, should we then hide the views?
// Set up subtitle
@@ -1069,22 +1049,15 @@ public class MediaControlPanel {
boolean shouldShowSubtitleText = !TextUtils.isEmpty(title);
CharSequence subtitleText = shouldShowSubtitleText ? subtitle : "";
subtitleView.setText(subtitleText);
- subtitleView.setTextColor(Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.textColorSecondary));
// TODO(b/223603970): If none of them have subtitles, should we then hide the views?
uiComponentIndex++;
}
mSmartspaceMediaItemsCount = uiComponentIndex;
- // Set up long press to show guts setting panel.
- mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
- mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
- logSmartspaceCardReported(
- 761 // SMARTSPACE_CARD_DISMISS
- );
+ // Guts
+ Runnable onDismissClickedRunnable = () -> {
closeGuts();
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L);
@@ -1104,7 +1077,12 @@ public class MediaControlPanel {
} else {
mBroadcastSender.sendBroadcast(dismissIntent);
}
- });
+ };
+ bindGutsMenuCommon(
+ /* isDismissible= */ true,
+ appName.toString(),
+ mRecommendationViewHolder.getGutsViewHolder(),
+ onDismissClickedRunnable);
mController = null;
if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) {
@@ -1112,6 +1090,74 @@ public class MediaControlPanel {
}
}
+ private void fetchAndUpdateRecommendationColors(Drawable appIcon) {
+ mBackgroundExecutor.execute(() -> {
+ ColorScheme colorScheme = new ColorScheme(
+ WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true);
+ mMainExecutor.execute(() -> setRecommendationColors(colorScheme));
+ });
+ }
+
+ private void setRecommendationColors(ColorScheme colorScheme) {
+ if (mRecommendationViewHolder == null) {
+ return;
+ }
+
+ int backgroundColor = MediaColorSchemesKt.surfaceFromScheme(colorScheme);
+ int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
+ int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
+
+ mRecommendationViewHolder.getRecommendations()
+ .setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
+ mRecommendationViewHolder.getMediaTitles().forEach(
+ (title) -> title.setTextColor(textPrimaryColor));
+ mRecommendationViewHolder.getMediaSubtitles().forEach(
+ (subtitle) -> subtitle.setTextColor(textSecondaryColor));
+
+ mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme);
+ }
+
+ private void bindGutsMenuCommon(
+ boolean isDismissible,
+ String appName,
+ GutsViewHolder gutsViewHolder,
+ Runnable onDismissClickedRunnable) {
+ // Text
+ String text;
+ if (isDismissible) {
+ text = mContext.getString(R.string.controls_media_close_session, appName);
+ } else {
+ text = mContext.getString(R.string.controls_media_active_session);
+ }
+ gutsViewHolder.getGutsText().setText(text);
+
+ // Dismiss button
+ gutsViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+ gutsViewHolder.getDismiss().setEnabled(isDismissible);
+ gutsViewHolder.getDismiss().setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+ logSmartspaceCardReported(SMARTSPACE_CARD_DISMISS_EVENT);
+ mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
+
+ onDismissClickedRunnable.run();
+ });
+
+ // Cancel button
+ gutsViewHolder.getCancel().setOnClickListener(v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ closeGuts();
+ }
+ });
+
+ // Settings button
+ gutsViewHolder.getSettings().setOnClickListener(v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
+ mActivityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */true);
+ }
+ });
+ }
+
/**
* Close the guts for this player.
*
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 5210499ca311..1437c965512e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -35,7 +35,8 @@ import javax.inject.Inject
class MediaViewController @Inject constructor(
private val context: Context,
private val configurationController: ConfigurationController,
- private val mediaHostStatesManager: MediaHostStatesManager
+ private val mediaHostStatesManager: MediaHostStatesManager,
+ private val logger: MediaViewLogger
) {
/**
@@ -260,10 +261,7 @@ class MediaViewController @Inject constructor(
TYPE.PLAYER -> MediaViewHolder.controlsIds
TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds
}
- val gutsIds = when (type) {
- TYPE.PLAYER -> MediaViewHolder.gutsIds
- TYPE.RECOMMENDATION -> RecommendationViewHolder.gutsIds
- }
+ val gutsIds = GutsViewHolder.ids
controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
// Make sure to use the unmodified state if guts are not visible.
@@ -330,6 +328,7 @@ class MediaViewController @Inject constructor(
// is cheap
setGutsViewState(result)
viewStates[cacheKey] = result
+ logger.logMediaSize("measured new viewState", result.width, result.height)
} else {
// This is an interpolated state
val startState = state.copy().also { it.expansion = 0.0f }
@@ -344,6 +343,7 @@ class MediaViewController @Inject constructor(
startViewState,
endViewState,
state.expansion)
+ logger.logMediaSize("interpolated viewState", result.width, result.height)
}
if (state.squishFraction < 1f) {
return squishViewState(result, state.squishFraction)
@@ -371,6 +371,7 @@ class MediaViewController @Inject constructor(
*/
fun attach(transitionLayout: TransitionLayout, type: TYPE) {
updateMediaViewControllerType(type)
+ logger.logMediaLocation("attach", currentStartLocation, currentEndLocation)
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
if (currentEndLocation == -1) {
@@ -409,6 +410,7 @@ class MediaViewController @Inject constructor(
currentEndLocation = endLocation
currentStartLocation = startLocation
currentTransitionProgress = transitionProgress
+ logger.logMediaLocation("setCurrentState", startLocation, endLocation)
val shouldAnimate = animateNextStateChange && !applyImmediately
@@ -461,6 +463,7 @@ class MediaViewController @Inject constructor(
result = layoutController.getInterpolatedState(startViewState, endViewState,
transitionProgress, tmpState)
}
+ logger.logMediaSize("setCurrentState", result.width, result.height)
layoutController.setState(result, applyImmediately, shouldAnimate, animationDuration,
animationDelay)
}
@@ -478,6 +481,7 @@ class MediaViewController @Inject constructor(
result.height = Math.max(it.measuredHeight, result.height)
result.width = Math.max(it.measuredWidth, result.width)
}
+ logger.logMediaSize("update to carousel", result.width, result.height)
return result
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index 8964d7114e74..5c93cdaeb0da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -56,13 +55,7 @@ class MediaViewHolder constructor(itemView: View) {
val scrubbingTotalTimeView: TextView =
itemView.requireViewById(R.id.media_scrubbing_total_time)
- // Settings screen
- val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
- val cancel = itemView.requireViewById<View>(R.id.cancel)
- val cancelText = itemView.requireViewById<TextView>(R.id.cancel_text)
- val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
- val dismissText = itemView.requireViewById<TextView>(R.id.dismiss_text)
- val settings = itemView.requireViewById<ImageButton>(R.id.settings)
+ val gutsViewHolder = GutsViewHolder(itemView)
// Action Buttons
val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
@@ -79,9 +72,9 @@ class MediaViewHolder constructor(itemView: View) {
init {
(player.background as IlluminationDrawable).let {
it.registerLightSource(seamless)
- it.registerLightSource(cancel)
- it.registerLightSource(dismiss)
- it.registerLightSource(settings)
+ it.registerLightSource(gutsViewHolder.cancel)
+ it.registerLightSource(gutsViewHolder.dismiss)
+ it.registerLightSource(gutsViewHolder.settings)
it.registerLightSource(actionPlayPause)
it.registerLightSource(actionNext)
it.registerLightSource(actionPrev)
@@ -122,12 +115,7 @@ class MediaViewHolder constructor(itemView: View) {
}
fun marquee(start: Boolean, delay: Long) {
- val longPressTextHandler = longPressText.getHandler()
- if (longPressTextHandler == null) {
- Log.d(TAG, "marquee while longPressText.getHandler() is null", Exception())
- return
- }
- longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
+ gutsViewHolder.marquee(start, delay, TAG)
}
companion object {
@@ -172,12 +160,7 @@ class MediaViewHolder constructor(itemView: View) {
R.id.media_scrubbing_elapsed_time,
R.id.media_scrubbing_total_time
)
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
- )
+
// Buttons used for notification-based actions
val genericButtonIds = setOf(
@@ -187,5 +170,17 @@ class MediaViewHolder constructor(itemView: View) {
R.id.action3,
R.id.action4
)
+
+ val expandedBottomActionIds = setOf(
+ R.id.actionPrev,
+ R.id.actionNext,
+ R.id.action0,
+ R.id.action1,
+ R.id.action2,
+ R.id.action3,
+ R.id.action4,
+ R.id.media_scrubbing_elapsed_time,
+ R.id.media_scrubbing_total_time
+ )
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewLogger.kt
new file mode 100644
index 000000000000..73868189b362
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewLogger.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.MediaViewLog
+import javax.inject.Inject
+
+private const val TAG = "MediaView"
+
+/**
+ * A buffered log for media view events that are too noisy for regular logging
+ */
+@SysUISingleton
+class MediaViewLogger @Inject constructor(
+ @MediaViewLog private val buffer: LogBuffer
+) {
+ fun logMediaSize(reason: String, width: Int, height: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ int1 = width
+ int2 = height
+ },
+ {
+ "size ($str1): $int1 x $int2"
+ }
+ )
+ }
+
+ fun logMediaLocation(reason: String, startLocation: Int, endLocation: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ int1 = startLocation
+ int2 = endLocation
+ },
+ {
+ "location ($str1): $int1 -> $int2"
+ }
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
index a83984036f60..52ac4e0682a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
@@ -24,6 +24,8 @@ import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
+private const val TAG = "RecommendationViewHolder"
+
/** ViewHolder for a Smartspace media recommendation. */
class RecommendationViewHolder private constructor(itemView: View) {
@@ -52,26 +54,19 @@ class RecommendationViewHolder private constructor(itemView: View) {
itemView.requireViewById(R.id.media_subtitle3)
)
- // Settings/Guts screen
- val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
- val cancel = itemView.requireViewById<View>(R.id.cancel)
- val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
- val dismissLabel = dismiss.getChildAt(0)
- val settings = itemView.requireViewById<View>(R.id.settings)
- val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
+ val gutsViewHolder = GutsViewHolder(itemView)
init {
(recommendations.background as IlluminationDrawable).let { background ->
mediaCoverContainers.forEach { background.registerLightSource(it) }
- background.registerLightSource(cancel)
- background.registerLightSource(dismiss)
- background.registerLightSource(dismissLabel)
- background.registerLightSource(settings)
+ background.registerLightSource(gutsViewHolder.cancel)
+ background.registerLightSource(gutsViewHolder.dismiss)
+ background.registerLightSource(gutsViewHolder.settings)
}
}
fun marquee(start: Boolean, delay: Long) {
- longPressText.getHandler().postDelayed({ longPressText.setSelected(start) }, delay)
+ gutsViewHolder.marquee(start, delay, TAG)
}
companion object {
@@ -104,14 +99,12 @@ class RecommendationViewHolder private constructor(itemView: View) {
R.id.media_cover1_container,
R.id.media_cover2_container,
R.id.media_cover3_container,
- )
-
- // Res Ids for the components on the guts panel.
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
+ R.id.media_title1,
+ R.id.media_title2,
+ R.id.media_title3,
+ R.id.media_subtitle1,
+ R.id.media_subtitle2,
+ R.id.media_subtitle3
)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 5218492ec4bf..193166b8d331 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -76,7 +76,11 @@ class SeekBarViewModel @Inject constructor(
) {
private var _data = Progress(false, false, false, false, null, 0)
set(value) {
+ val enabledChanged = value.enabled != field.enabled
field = value
+ if (enabledChanged) {
+ enabledChangeListener?.onEnabledChanged(value.enabled)
+ }
_progress.postValue(value)
}
private val _progress = MutableLiveData<Progress>().apply {
@@ -122,6 +126,7 @@ class SeekBarViewModel @Inject constructor(
}
private var scrubbingChangeListener: ScrubbingChangeListener? = null
+ private var enabledChangeListener: EnabledChangeListener? = null
/** Set to true when the user is touching the seek bar to change the position. */
private var scrubbing = false
@@ -136,8 +141,6 @@ class SeekBarViewModel @Inject constructor(
lateinit var logSeek: () -> Unit
- fun getEnabled() = _data.enabled
-
/**
* Event indicating that the user has started interacting with the seek bar.
*/
@@ -189,6 +192,9 @@ class SeekBarViewModel @Inject constructor(
/**
* Updates media information.
+ *
+ * This function makes a binder call, so it must happen on a worker thread.
+ *
* @param mediaController controller for media session
*/
@WorkerThread
@@ -232,6 +238,7 @@ class SeekBarViewModel @Inject constructor(
cancel?.run()
cancel = null
scrubbingChangeListener = null
+ enabledChangeListener = null
}
@WorkerThread
@@ -279,11 +286,26 @@ class SeekBarViewModel @Inject constructor(
}
}
+ fun setEnabledChangeListener(listener: EnabledChangeListener) {
+ enabledChangeListener = listener
+ }
+
+ fun removeEnabledChangeListener(listener: EnabledChangeListener) {
+ if (listener == enabledChangeListener) {
+ enabledChangeListener = null
+ }
+ }
+
/** Listener interface to be notified when the user starts or stops scrubbing. */
interface ScrubbingChangeListener {
fun onScrubbingChanged(scrubbing: Boolean)
}
+ /** Listener interface to be notified when the seekbar's enabled status changes. */
+ interface EnabledChangeListener {
+ fun onEnabledChanged(enabled: Boolean)
+ }
+
private class SeekBarChangeListener(
val viewModel: SeekBarViewModel
) : SeekBar.OnSeekBarChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index bde772d95674..e5e7eb66630b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -19,7 +19,10 @@ package com.android.systemui.media.dialog;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import android.annotation.NonNull;
import android.app.WallpaperColors;
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -59,6 +62,9 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
/**
* Base dialog for media output UI
*/
@@ -69,6 +75,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private static final String EMPTY_TITLE = " ";
private static final String PREF_NAME = "MediaOutputDialog";
private static final String PREF_IS_LE_BROADCAST_FIRST_LAUNCH = "PrefIsLeBroadcastFirstLaunch";
+ private static final boolean DEBUG = true;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final RecyclerView.LayoutManager mLayoutManager;
@@ -91,6 +98,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private Button mAppButton;
private int mListMaxHeight;
private WallpaperColors mWallpaperColors;
+ private Executor mExecutor;
MediaOutputBaseAdapter mAdapter;
@@ -103,6 +111,79 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
}
};
+ private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+ new BluetoothLeBroadcast.Callback() {
+ @Override
+ public void onBroadcastStarted(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStarted(), reason = " + reason
+ + ", broadcastId = " + broadcastId);
+ }
+ mMainThreadHandler.post(() -> startLeBroadcastDialog());
+ }
+
+ @Override
+ public void onBroadcastStartFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
+ }
+ handleLeBroadcastStartFailed();
+ }
+
+ @Override
+ public void onBroadcastMetadataChanged(int broadcastId,
+ @NonNull BluetoothLeBroadcastMetadata metadata) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId
+ + ", metadata = " + metadata);
+ }
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onBroadcastStopped(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopped(), reason = " + reason
+ + ", broadcastId = " + broadcastId);
+ }
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onBroadcastStopFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
+ }
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onBroadcastUpdated(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastUpdated(), reason = " + reason
+ + ", broadcastId = " + broadcastId);
+ }
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onBroadcastUpdateFailed(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastUpdateFailed(), reason = " + reason
+ + ", broadcastId = " + broadcastId);
+ }
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onPlaybackStarted(int reason, int broadcastId) {
+ }
+
+ @Override
+ public void onPlaybackStopped(int reason, int broadcastId) {
+ }
+ };
+
public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
MediaOutputController mediaOutputController) {
super(context, R.style.Theme_SystemUI_Dialog_Media);
@@ -114,6 +195,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mLayoutManager = new LinearLayoutManager(mContext);
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.media_output_dialog_list_max_height);
+ mExecutor = Executors.newSingleThreadExecutor();
}
@Override
@@ -171,11 +253,18 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
public void onStart() {
super.onStart();
mMediaOutputController.start(this);
+ if(isBroadcastSupported()) {
+ mMediaOutputController.registerLeBroadcastServiceCallBack(mExecutor,
+ mBroadcastCallback);
+ }
}
@Override
public void onStop() {
super.onStop();
+ if(isBroadcastSupported()) {
+ mMediaOutputController.unregisterLeBroadcastServiceCallBack(mBroadcastCallback);
+ }
mMediaOutputController.stop();
}
@@ -254,35 +343,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mAdapter.notifyDataSetChanged();
}
}
- // Show when remote media session is available
+ // Show when remote media session is available or
+ // when the device supports BT LE audio + media is playing
mStopButton.setVisibility(getStopButtonVisibility());
- if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
- mStopButton.setText(R.string.media_output_broadcast);
- mStopButton.setOnClickListener(v -> {
- SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
- Context.MODE_PRIVATE);
-
- if (sharedPref != null
- && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
- Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");
-
- mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
- mBroadcastSender,
- MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
- editor.apply();
- } else {
- mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
- mBroadcastSender);
- }
- });
- } else {
- mStopButton.setOnClickListener(v -> {
- mMediaOutputController.releaseSession();
- dismiss();
- });
- }
+ mStopButton.setEnabled(true);
+ mStopButton.setText(getStopButtonText());
+ mStopButton.setOnClickListener(v -> onStopButtonClick());
}
private Drawable resizeDrawable(Drawable drawable, int size) {
@@ -301,6 +367,56 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
Bitmap.createScaledBitmap(bitmap, size, size, false));
}
+ protected void handleLeBroadcastStartFailed() {
+ mStopButton.setText(R.string.media_output_broadcast_start_failed);
+ mStopButton.setEnabled(false);
+ mMainThreadHandler.postDelayed(() -> refresh(), 3000);
+ }
+
+ protected void startLeBroadcast() {
+ mStopButton.setText(R.string.media_output_broadcast_starting);
+ mStopButton.setEnabled(false);
+ if (!mMediaOutputController.startBluetoothLeBroadcast()) {
+ // If the system can't execute "broadcast start", then UI shows the error.
+ handleLeBroadcastStartFailed();
+ }
+ }
+
+ protected boolean startLeBroadcastDialogForFirstTime(){
+ SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
+ Context.MODE_PRIVATE);
+ if (sharedPref != null
+ && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
+ Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");
+
+ mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
+ mBroadcastSender,
+ MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH,
+ (d, w) -> {
+ startLeBroadcast();
+ });
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
+ editor.apply();
+ return true;
+ }
+ return false;
+ }
+
+ protected void startLeBroadcastDialog() {
+ mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
+ mBroadcastSender);
+ refresh();
+ }
+
+ protected void stopLeBroadcast() {
+ mStopButton.setEnabled(false);
+ if (!mMediaOutputController.stopBluetoothLeBroadcast()) {
+ // If the system can't execute "broadcast stop", then UI does refresh.
+ mMainThreadHandler.post(() -> refresh());
+ }
+ }
+
abstract Drawable getAppSourceIcon();
abstract int getHeaderIconRes();
@@ -315,6 +431,15 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
abstract int getStopButtonVisibility();
+ public CharSequence getStopButtonText() {
+ return mContext.getText(R.string.keyboard_key_media_stop);
+ }
+
+ public void onStopButtonClick() {
+ mMediaOutputController.releaseSession();
+ dismiss();
+ }
+
public boolean isBroadcastSupported() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 494dae0532dc..9b3b3ce6109f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -142,8 +142,11 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
mBroadcastNotify = getDialogView().requireViewById(R.id.broadcast_info);
mBroadcastNotify.setOnClickListener(v -> {
- mMediaOutputController.launchLeBroadcastNotifyDialog(null, null,
- MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON);
+ mMediaOutputController.launchLeBroadcastNotifyDialog(
+ /* view= */ null,
+ /* broadcastSender= */ null,
+ MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON,
+ /* onClickListener= */ null);
});
mBroadcastName = getDialogView().requireViewById(R.id.broadcast_name_summary);
mBroadcastNameEdit = getDialogView().requireViewById(R.id.broadcast_name_edit);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index ec2a950051b7..0fbec3baffa6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -18,10 +18,13 @@ package com.android.systemui.media.dialog;
import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
+import android.annotation.CallbackExecutor;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.WallpaperColors;
+import android.bluetooth.BluetoothLeBroadcast;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -58,7 +61,7 @@ import androidx.mediarouter.media.MediaRouterParams;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.InfoMediaManager;
@@ -83,6 +86,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -642,17 +646,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
}
void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
- BroadcastNotifyDialog action) {
+ BroadcastNotifyDialog action, final DialogInterface.OnClickListener listener) {
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
switch (action) {
case ACTION_FIRST_LAUNCH:
builder.setTitle(R.string.media_output_first_broadcast_title);
builder.setMessage(R.string.media_output_first_notify_broadcast_message);
builder.setNegativeButton(android.R.string.cancel, null);
- builder.setPositiveButton(R.string.media_output_broadcast,
- (d, w) -> {
- launchMediaOutputBroadcastDialog(mediaOutputDialog, broadcastSender);
- });
+ builder.setPositiveButton(R.string.media_output_broadcast, listener);
break;
case ACTION_BROADCAST_INFO_ICON:
builder.setTitle(R.string.media_output_broadcast);
@@ -685,18 +686,64 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|| features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
}
- boolean isBluetoothLeDevice(@NonNull MediaDevice device) {
- if (device instanceof BluetoothMediaDevice) {
- final CachedBluetoothDevice cachedDevice =
- ((BluetoothMediaDevice) device).getCachedDevice();
- boolean isConnectedLeAudioDevice =
- (cachedDevice != null) ? cachedDevice.isConnectedLeAudioDevice() : false;
- if (DEBUG) {
- Log.d(TAG, "isConnectedLeAudioDevice=" + isConnectedLeAudioDevice);
- }
- return isConnectedLeAudioDevice;
+ boolean isBroadcastSupported() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ return broadcast != null ? true : false;
+ }
+
+ boolean isBluetoothLeBroadcastEnabled() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ return false;
}
- return false;
+ return broadcast.isEnabled(null);
+ }
+
+ boolean startBluetoothLeBroadcast() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "The broadcast profile is null");
+ return false;
+ }
+ broadcast.startBroadcast(getAppSourceName(), /*language*/ null);
+ return true;
+ }
+
+ boolean stopBluetoothLeBroadcast() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "The broadcast profile is null");
+ return false;
+ }
+ broadcast.stopLatestBroadcast();
+ return true;
+ }
+
+ void registerLeBroadcastServiceCallBack(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BluetoothLeBroadcast.Callback callback) {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "The broadcast profile is null");
+ return;
+ }
+ broadcast.registerServiceCallBack(executor, callback);
+ }
+
+ void unregisterLeBroadcastServiceCallBack(
+ @NonNull BluetoothLeBroadcast.Callback callback) {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "The broadcast profile is null");
+ return;
+ }
+ broadcast.unregisterServiceCallBack(callback);
}
private boolean isPlayBackInfoLocal() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index fc1039780419..9248433a4a90 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,7 +27,6 @@ import androidx.core.graphics.drawable.IconCompat;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
@@ -93,18 +92,41 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
isActiveRemoteDevice = mMediaOutputController.isActiveRemoteDevice(
mMediaOutputController.getCurrentConnectedMediaDevice());
}
- boolean isBroadCastSupported = isBroadcastSupported();
+ boolean showBroadcastButton = isBroadcastSupported() && mMediaOutputController.isPlaying();
- return (isActiveRemoteDevice || isBroadCastSupported) ? View.VISIBLE : View.GONE;
+ return (isActiveRemoteDevice || showBroadcastButton) ? View.VISIBLE : View.GONE;
}
@Override
public boolean isBroadcastSupported() {
- MediaDevice device = mMediaOutputController.getCurrentConnectedMediaDevice();
- if (device == null) {
- return false;
+ return mMediaOutputController.isBroadcastSupported();
+ }
+
+ @Override
+ public CharSequence getStopButtonText() {
+ int resId = R.string.keyboard_key_media_stop;
+ if (isBroadcastSupported() && mMediaOutputController.isPlaying()
+ && !mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
+ resId = R.string.media_output_broadcast;
+ }
+ return mContext.getText(resId);
+ }
+
+ @Override
+ public void onStopButtonClick() {
+ if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
+ if (!mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
+ if (startLeBroadcastDialogForFirstTime()) {
+ return;
+ }
+ startLeBroadcast();
+ } else {
+ stopLeBroadcast();
+ }
+ } else {
+ mMediaOutputController.releaseSession();
+ dismiss();
}
- return mMediaOutputController.isBluetoothLeDevice(device);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 740ecff6b06e..72488f3dc823 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -197,7 +197,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
private final DeviceConfigProxy mDeviceConfigProxy;
- private final NavigationBarTransitions mNavigationBarTransitions;
private final Optional<BackAnimation> mBackAnimation;
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
@@ -514,7 +513,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
InputMethodManager inputMethodManager,
DeadZone deadZone,
DeviceConfigProxy deviceConfigProxy,
- NavigationBarTransitions navigationBarTransitions,
Optional<BackAnimation> backAnimation) {
super(navigationBarView);
mFrame = navigationBarFrame;
@@ -539,7 +537,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mRecentsOptional = recentsOptional;
mDeadZone = deadZone;
mDeviceConfigProxy = deviceConfigProxy;
- mNavigationBarTransitions = navigationBarTransitions;
mBackAnimation = backAnimation;
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
@@ -564,7 +561,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
public void onInit() {
// TODO: A great deal of this code should probably live in onViewAttached.
// It should also has corresponding cleanup in onViewDetached.
- mView.setBarTransitions(mNavigationBarTransitions);
mView.setTouchHandler(mTouchHandler);
mView.setNavBarMode(mNavBarMode);
mView.updateRotationButton();
@@ -636,7 +632,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.setOnVerticalChangedListener(this::onVerticalChanged);
mView.setOnTouchListener(this::onNavigationTouch);
if (mSavedState != null) {
- getBarTransitions().getLightTransitionsController().restoreState(mSavedState);
+ mView.getLightTransitionsController().restoreState(mSavedState);
}
setNavigationIconHints(mNavigationIconHints);
mView.setWindowVisible(isNavBarWindowVisible());
@@ -709,7 +705,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.getRotationButtonController();
rotationButtonController.setRotationCallback(null);
mView.setUpdateActiveTouchRegionsCallback(null);
- getBarTransitions().destroy();
+ mView.getBarTransitions().destroy();
+ mView.getLightTransitionsController().destroy(mContext);
mOverviewProxyService.removeCallback(mOverviewProxyListener);
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
if (mOrientationHandle != null) {
@@ -735,7 +732,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
outState.putInt(EXTRA_APPEARANCE, mAppearance);
outState.putInt(EXTRA_BEHAVIOR, mBehavior);
outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
- getBarTransitions().getLightTransitionsController().saveState(outState);
+ mView.getLightTransitionsController().saveState(outState);
}
/**
@@ -896,7 +893,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
pw.println(" mTransientShown=" + mTransientShown);
pw.println(" mTransientShownFromGestureOnSystemBar="
+ mTransientShownFromGestureOnSystemBar);
- dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions());
+ dumpBarTransitions(pw, "mNavigationBarView", mView.getBarTransitions());
mView.dump(pw);
}
@@ -1433,7 +1430,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mLightBarController = lightBarController;
if (mLightBarController != null) {
mLightBarController.setNavigationBar(
- getBarTransitions().getLightTransitionsController());
+ mView.getLightTransitionsController());
}
}
@@ -1475,7 +1472,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
.orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
- getBarTransitions().transitionTo(mTransitionMode, anim);
+ mView.getBarTransitions().transitionTo(mTransitionMode, anim);
}
public void disableAnimationsDuringHide(long delay) {
@@ -1495,11 +1492,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
public NavigationBarTransitions getBarTransitions() {
- return mNavigationBarTransitions;
+ return mView.getBarTransitions();
}
public void finishBarAnimations() {
- getBarTransitions().finishAnimations();
+ mView.getBarTransitions().finishAnimations();
}
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index e625501d8961..58e07db09c62 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -31,19 +31,17 @@ import android.view.View;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import javax.inject.Inject;
-
-/** */
-@NavigationBarScope
public final class NavigationBarTransitions extends BarTransitions implements
LightBarTransitionsController.DarkIntensityApplier {
@@ -83,13 +81,15 @@ public final class NavigationBarTransitions extends BarTransitions implements
}
};
- @Inject
- public NavigationBarTransitions(
- NavigationBarView view,
- LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
+ public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) {
super(view, R.drawable.nav_background);
mView = view;
- mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
+ mLightTransitionsController = new LightBarTransitionsController(
+ view.getContext(),
+ this,
+ commandQueue,
+ Dependency.get(KeyguardStateController.class),
+ Dependency.get(StatusBarStateController.class));
mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
mDarkIntensityListeners = new ArrayList();
@@ -127,7 +127,6 @@ public final class NavigationBarTransitions extends BarTransitions implements
Display.DEFAULT_DISPLAY);
} catch (RemoteException e) {
}
- mLightTransitionsController.destroy();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 8878c2decb87..abff914693d4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -85,6 +85,7 @@ import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -139,7 +140,7 @@ public class NavigationBarView extends FrameLayout {
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
private boolean mDeadZoneConsuming = false;
- private NavigationBarTransitions mBarTransitions;
+ private final NavigationBarTransitions mBarTransitions;
@Nullable
private AutoHideController mAutoHideController;
@@ -369,6 +370,7 @@ public class NavigationBarView extends FrameLayout {
mConfiguration.updateFrom(context.getResources().getConfiguration());
mScreenPinningNotify = new ScreenPinningNotify(mContext);
+ mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
@@ -416,14 +418,14 @@ public class NavigationBarView extends FrameLayout {
}
}
- void setBarTransitions(NavigationBarTransitions navigationBarTransitions) {
- mBarTransitions = navigationBarTransitions;
- }
-
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
}
+ public NavigationBarTransitions getBarTransitions() {
+ return mBarTransitions;
+ }
+
public LightBarTransitionsController getLightTransitionsController() {
return mBarTransitions.getLightTransitionsController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 363baaa5ef70..cdc6b3b89f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -264,7 +264,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
- mLightBarTransitionsController.destroy();
+ mLightBarTransitionsController.destroy(mContext);
mLightBarController.setNavigationBar(null);
mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
mInitialized = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 292b911e1f7d..bcd8e594ffbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -138,6 +138,7 @@ public class NotifCollection implements Dumpable {
private final NotifCollectionLogger mLogger;
private final Handler mMainHandler;
private final LogBufferEulogizer mEulogizer;
+ private final DumpManager mDumpManager;
private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
@@ -163,15 +164,13 @@ public class NotifCollection implements Dumpable {
@Main Handler mainHandler,
LogBufferEulogizer logBufferEulogizer,
DumpManager dumpManager) {
- Assert.isMainThread();
mStatusBarService = statusBarService;
mClock = clock;
mNotifPipelineFlags = notifPipelineFlags;
mLogger = logger;
mMainHandler = mainHandler;
mEulogizer = logBufferEulogizer;
-
- dumpManager.registerDumpable(TAG, this);
+ mDumpManager = dumpManager;
}
/** Initializes the NotifCollection and registers it to receive notification events. */
@@ -181,7 +180,7 @@ public class NotifCollection implements Dumpable {
throw new RuntimeException("attach() called twice");
}
mAttached = true;
-
+ mDumpManager.registerDumpable(TAG, this);
groupCoalescer.setNotificationHandler(mNotifHandler);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 1155fe214426..51af9559eda2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -91,6 +91,7 @@ public class ShadeListBuilder implements Dumpable {
private final SystemClock mSystemClock;
private final ShadeListBuilderLogger mLogger;
private final NotificationInteractionTracker mInteractionTracker;
+ private final DumpManager mDumpManager;
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
private final boolean mAlwaysLogList;
@@ -133,14 +134,12 @@ public class ShadeListBuilder implements Dumpable {
ShadeListBuilderLogger logger,
SystemClock systemClock
) {
- Assert.isMainThread();
mSystemClock = systemClock;
mLogger = logger;
mAlwaysLogList = flags.isDevLoggingEnabled();
mInteractionTracker = interactionTracker;
mChoreographer = pipelineChoreographer;
- dumpManager.registerDumpable(TAG, this);
-
+ mDumpManager = dumpManager;
setSectioners(Collections.emptyList());
}
@@ -150,6 +149,7 @@ public class ShadeListBuilder implements Dumpable {
*/
public void attach(NotifCollection collection) {
Assert.isMainThread();
+ mDumpManager.registerDumpable(TAG, this);
collection.addCollectionListener(mInteractionTracker);
collection.setBuildListener(mReadyForBuildListener);
mChoreographer.addOnEvalListener(this::buildList);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 673c1a66b0da..18877f9fb437 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -233,9 +233,6 @@ public class KeyguardStatusBarView extends RelativeLayout {
LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
- int marginStart = getResources().getDimensionPixelSize(
- R.dimen.system_icons_super_container_margin_start);
-
// Use status_bar_padding_end to replace original
// system_icons_super_container_avatarless_margin_end to prevent different end alignment
// between PhoneStatusBarView and KeyguardStatusBarView
@@ -248,8 +245,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
// 1. status bar layout: mPadding(consider round_corner + privacy dot)
// 2. icon container: R.dimen.status_bar_padding_end
- if (marginEnd != lp.getMarginEnd() || marginStart != lp.getMarginStart()) {
- lp.setMarginStart(marginStart);
+ if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
mSystemIconsContainer.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 16fddb420fc4..b6ad9f704bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -93,8 +93,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
mDisplayId = mContext.getDisplayId();
}
- /** Call to cleanup the LightBarTransitionsController when done with it. */
- public void destroy() {
+ public void destroy(Context context) {
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index fc20ac241e38..8f2a432d0ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -138,7 +138,7 @@ constructor(
ensureOverlayRemoved()
- val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
+ val newRoot = SurfaceControlViewHost(context, context.display!!, wwm, false)
val newView =
LightRevealScrim(context, null).apply {
revealEffect = createLightRevealEffect()
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index 323db5cfd249..bc9e5963c22d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -21,9 +21,11 @@ import android.os.Handler;
import android.os.Looper;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import java.util.Optional;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -73,6 +75,18 @@ public abstract class GlobalConcurrencyModule {
}
/**
+ * Provide an Executor specifically for running UI operations on a separate thread.
+ *
+ * Keep submitted runnables short and to the point, just as with any other UI code.
+ */
+ @Provides
+ @Singleton
+ @UiBackground
+ public static Executor provideUiBackgroundExecutor() {
+ return Executors.newSingleThreadExecutor();
+ }
+
+ /**
* Provide a Main-Thread Executor.
*/
@Provides
@@ -92,7 +106,6 @@ public abstract class GlobalConcurrencyModule {
return new ExecutorImpl(looper);
}
-
/** */
@Binds
@Singleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index d8d73db2ca8c..7c211b22e657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -213,6 +213,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mExecutor.runAllReady();
}
});
+ doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
reset(mTunerService);
try {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
index 30bff0943da7..89389b0dd424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
@@ -44,11 +44,13 @@ public class MockMagnificationAnimationCallback extends IRemoteMagnificationAnim
@Override
public void onResult(boolean success) throws RemoteException {
- mCountDownLatch.countDown();
if (success) {
mSuccessCount.getAndIncrement();
} else {
mFailedCount.getAndIncrement();
}
+ // It should be put at the last line to avoid making CountDownLatch#await passed without
+ // updating values.
+ mCountDownLatch.countDown();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 18ba7dc020e2..b7d345965d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -80,7 +80,6 @@ import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -319,7 +318,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
}
- @Ignore("b/224717753")
@Test
public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
throws InterruptedException {
@@ -354,7 +352,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
}
- @Ignore("b/224717753")
@Test
public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
throws InterruptedException {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
index e027a2b7bcaf..558261b31eb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
@@ -87,6 +87,17 @@ public class AccessibilityFloatingMenuTest extends SysuiTestCase {
assertThat(mMenuView.isShowing()).isFalse();
}
+ @Test
+ public void showMenuView_emptyTarget_notShow() {
+ final List<String> emptyTargets = new ArrayList<>();
+ doReturn(emptyTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
+ anyInt());
+
+ mMenu.show();
+
+ assertThat(mMenuView.isShowing()).isFalse();
+ }
+
@After
public void tearDown() {
mMenu.hide();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 28da2f13eb69..03b18ae66d4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -27,6 +27,7 @@ import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -46,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
@@ -185,6 +187,8 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDialog1.getRequestId()).thenReturn(REQUEST_ID);
when(mDialog2.getRequestId()).thenReturn(REQUEST_ID);
+ when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point());
+
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -663,7 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSubscribesToOrientationChangesWhenShowingDialog() {
showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
- verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
+ verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
mAuthController.hideAuthenticationDialog(REQUEST_ID);
verify(mDisplayManager).unregisterDisplayListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 5440b45a778f..7f8656c1ecbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -163,7 +163,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() {
// GIVEN fp exists & user doesn't need strong auth
val fpsLocation = PointF(5f, 5f)
- `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
+ `when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
@@ -185,7 +185,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
fun testFingerprintTrigger_StrongAuthRequired_NoRipple() {
// GIVEN fp exists & keyguard is visible
val fpsLocation = PointF(5f, 5f)
- `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
+ `when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
index 40f335dfc20d..69c7f364d235 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
@@ -20,6 +20,7 @@ import static com.android.systemui.biometrics.BiometricDisplayListener.SensorTyp
import static com.android.systemui.biometrics.BiometricDisplayListener.SensorType.UnderDisplayFingerprint;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -89,7 +90,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
mContextSpy, mDisplayManager, mHandler, mUdfpsType, mOnChangedCallback);
listener.enable();
- verify(mDisplayManager).registerDisplayListener(any(), same(mHandler));
+ verify(mDisplayManager).registerDisplayListener(any(), same(mHandler),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
}
@Test
@@ -113,7 +115,7 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_90);
listener.enable();
verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
- same(mHandler));
+ same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// Rotate the device back to portrait and ensure the rotation is detected.
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
@@ -150,8 +152,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
// The listener should record the current rotation and register a display listener.
verify(mDisplay).getRotation();
- verify(mDisplayManager)
- .registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler));
+ verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
+ same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// Test the first rotation since the listener was enabled.
mDisplayListenerCaptor.getValue().onDisplayChanged(123);
@@ -182,8 +184,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
listener.enable();
// The listener should register a display listener.
- verify(mDisplayManager)
- .registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler));
+ verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
+ same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
// mOnChangedCallback should be invoked for all calls to onDisplayChanged.
mDisplayListenerCaptor.getValue().onDisplayChanged(123);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 839c0ab1318f..e1a348ead9b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -188,7 +188,7 @@ class SidefpsControllerTest : SysuiTestCase() {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
- verify(displayManager).registerDisplayListener(any(), eq(handler))
+ verify(displayManager).registerDisplayListener(any(), eq(handler), anyLong())
overlayController.hide(SENSOR_ID)
executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index fd49766dafef..a57b011d7125 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics
+import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
@@ -23,7 +24,6 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
-import android.hardware.biometrics.SensorLocationInternal
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.AndroidTestingRunner
@@ -31,6 +31,8 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
+import android.view.Surface
+import android.view.Surface.Rotation
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest
@@ -53,8 +55,10 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
@@ -63,13 +67,18 @@ import org.mockito.Mockito.`when` as whenever
private const val REQUEST_ID = 2L
+// Dimensions for the current display resolution.
+private const val DISPLAY_WIDTH = 1080
+private const val DISPLAY_HEIGHT = 1920
+private const val SENSOR_WIDTH = 30
+private const val SENSOR_HEIGHT = 60
+
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class UdfpsControllerOverlayTest : SysuiTestCase() {
- @JvmField @Rule
- var rule = MockitoJUnit.rule()
+ @JvmField @Rule var rule = MockitoJUnit.rule()
@Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var inflater: LayoutInflater
@@ -85,18 +94,17 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
+ @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
@Mock private lateinit var hbmProvider: UdfpsHbmProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+ @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
- private val sensorProps = SensorLocationInternal("", 10, 100, 20)
- .asFingerprintSensorProperties()
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
+ private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
private lateinit var controllerOverlay: UdfpsControllerOverlay
@Before
@@ -121,7 +129,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController,
- unlockedScreenOffAnimationController, sensorProps, hbmProvider, REQUEST_ID, reason,
+ unlockedScreenOffAnimationController, hbmProvider, REQUEST_ID, reason,
controllerCallback, onTouch, activityLaunchAnimator)
block()
}
@@ -148,12 +156,96 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test
fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
+ private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
+ // Sensor that's in the top left corner of the display in natural orientation.
+ val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
+ overlayParams = UdfpsOverlayParams(
+ sensorBounds,
+ DISPLAY_WIDTH,
+ DISPLAY_HEIGHT,
+ scaleFactor = 1f,
+ rotation
+ )
+ block()
+ }
+
+ @Test
+ fun showUdfpsOverlay_withRotation0() = withRotation(Surface.ROTATION_0) {
+ withReason(REASON_AUTH_BP) {
+ controllerOverlay.show(udfpsController, overlayParams)
+ verify(windowManager).addView(
+ eq(controllerOverlay.overlayView),
+ layoutParamsCaptor.capture()
+ )
+
+ // ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
+ val lp = layoutParamsCaptor.value
+ assertThat(lp.x).isEqualTo(0)
+ assertThat(lp.y).isEqualTo(0)
+ assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ }
+ }
+
+ @Test
+ fun showUdfpsOverlay_withRotation180() = withRotation(Surface.ROTATION_180) {
+ withReason(REASON_AUTH_BP) {
+ controllerOverlay.show(udfpsController, overlayParams)
+ verify(windowManager).addView(
+ eq(controllerOverlay.overlayView),
+ layoutParamsCaptor.capture()
+ )
+
+ // ROTATION_180 is not supported. Sensor should stay in the top left corner.
+ val lp = layoutParamsCaptor.value
+ assertThat(lp.x).isEqualTo(0)
+ assertThat(lp.y).isEqualTo(0)
+ assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ }
+ }
+
+ @Test
+ fun showUdfpsOverlay_withRotation90() = withRotation(Surface.ROTATION_90) {
+ withReason(REASON_AUTH_BP) {
+ controllerOverlay.show(udfpsController, overlayParams)
+ verify(windowManager).addView(
+ eq(controllerOverlay.overlayView),
+ layoutParamsCaptor.capture()
+ )
+
+ // Sensor should be in the bottom left corner in ROTATION_90.
+ val lp = layoutParamsCaptor.value
+ assertThat(lp.x).isEqualTo(0)
+ assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH)
+ assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ }
+ }
+
+ @Test
+ fun showUdfpsOverlay_withRotation270() = withRotation(Surface.ROTATION_270) {
+ withReason(REASON_AUTH_BP) {
+ controllerOverlay.show(udfpsController, overlayParams)
+ verify(windowManager).addView(
+ eq(controllerOverlay.overlayView),
+ layoutParamsCaptor.capture()
+ )
+
+ // Sensor should be in the top right corner in ROTATION_270.
+ val lp = layoutParamsCaptor.value
+ assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT)
+ assertThat(lp.y).isEqualTo(0)
+ assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ }
+ }
+
private fun showUdfpsOverlay(isEnrollUseCase: Boolean = false) {
- val didShow = controllerOverlay.show(udfpsController)
+ val didShow = controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
verify(udfpsView).setHbmProvider(eq(hbmProvider))
- verify(udfpsView).sensorProperties = eq(sensorProps)
verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any())
@@ -162,7 +254,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
assertThat(controllerOverlay.isHiding).isFalse()
assertThat(controllerOverlay.overlayView).isNotNull()
if (isEnrollUseCase) {
- verify(udfpsEnrollView).updateSensorLocation(eq(sensorProps))
+ verify(udfpsEnrollView).updateSensorLocation(eq(overlayParams.sensorBounds))
assertThat(controllerOverlay.enrollHelper).isNotNull()
} else {
assertThat(controllerOverlay.enrollHelper).isNull()
@@ -188,7 +280,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
private fun hideUdfpsOverlay() {
- val didShow = controllerOverlay.show(udfpsController)
+ val didShow = controllerOverlay.show(udfpsController, overlayParams)
val view = controllerOverlay.overlayView
val didHide = controllerOverlay.hide()
@@ -209,13 +301,13 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test
fun canNotReshow() = withReason(REASON_AUTH_BP) {
- assertThat(controllerOverlay.show(udfpsController)).isTrue()
- assertThat(controllerOverlay.show(udfpsController)).isFalse()
+ assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
+ assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
}
@Test
fun forwardEnrollProgressEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController)
+ controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentProgress(/* remaining */20)
@@ -228,7 +320,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test
fun forwardEnrollHelpEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController)
+ controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentHelp()
@@ -240,7 +332,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Test
fun forwardEnrollAcquiredEvents() = withReason(REASON_ENROLL_ENROLLING) {
- controllerOverlay.show(udfpsController)
+ controllerOverlay.show(udfpsController, overlayParams)
with(EnrollListener(controllerOverlay)) {
controllerOverlay.onEnrollmentProgress(/* remaining */ 1)
@@ -261,7 +353,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
whenever(udfpsView.isIlluminationRequested).thenReturn(true)
- controllerOverlay.show(udfpsController)
+ controllerOverlay.show(udfpsController, overlayParams)
controllerOverlay.hide()
verify(udfpsView).stopIllumination()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 5d624cdc47a1..80df1e3a7e5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.biometrics;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -28,11 +31,12 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
@@ -50,6 +54,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -67,7 +72,6 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -124,8 +128,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private CentralSurfaces mCentralSurfaces;
- @Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
private DumpManager mDumpManager;
@@ -174,18 +176,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
private UdfpsFpmOtherView mFpmOtherView;
@Mock
private UdfpsKeyguardView mKeyguardView;
- private UdfpsAnimationViewController mUdfpsKeyguardViewController =
+ private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
mock(UdfpsKeyguardViewController.class);
@Mock
- private TypedArray mBrightnessValues;
- @Mock
- private TypedArray mBrightnessBacklight;
- @Mock
private SystemUIDialogManager mSystemUIDialogManager;
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
- private AlternateUdfpsTouchProvider mTouchProvider;
+ private AlternateUdfpsTouchProvider mAlternateTouchProvider;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -198,7 +196,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Before
public void setUp() {
- setUpResources();
mExecution = new FakeExecution();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
@@ -260,22 +257,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
mSystemUIDialogManager,
mLatencyTracker,
mActivityLaunchAnimator,
- Optional.of(mTouchProvider));
+ Optional.of(mAlternateTouchProvider));
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
-
- assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId);
- }
-
- private void setUpResources() {
- when(mBrightnessValues.length()).thenReturn(2);
- when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
- when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
- when(mBrightnessBacklight.length()).thenReturn(2);
- when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
- when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams());
}
@Test
@@ -301,7 +288,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle();
@@ -362,7 +349,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN multiple touches are received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
@@ -395,7 +382,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
+ verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
@@ -404,6 +391,175 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
+ public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
+ final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
+ final int[] displayWidth = new int[]{1080, 1440};
+ final int[] displayHeight = new int[]{1920, 2560};
+ final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
+ final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
+ final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
+ displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
+
+ for (int i1 = 0; i1 <= 1; ++i1)
+ for (int i2 = 0; i2 <= 1; ++i2)
+ for (int i3 = 0; i3 <= 1; ++i3)
+ for (int i4 = 0; i4 <= 1; ++i4)
+ for (int i5 = 0; i5 <= 1; ++i5) {
+ final UdfpsOverlayParams newParams = new UdfpsOverlayParams(sensorBounds[i1],
+ displayWidth[i2], displayHeight[i3], scaleFactor[i4], rotation[i5]);
+
+ if (newParams.equals(oldParams)) {
+ continue;
+ }
+
+ // Initialize the overlay with old parameters.
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, oldParams);
+
+ // Show the overlay.
+ reset(mWindowManager);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
+ mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).addView(any(), any());
+
+ // Update overlay parameters.
+ reset(mWindowManager);
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, newParams);
+ mFgExecutor.runAllReady();
+
+ // Ensure the overlay was recreated.
+ verify(mWindowManager).removeView(any());
+ verify(mWindowManager).addView(any(), any());
+ }
+ }
+
+ @Test
+ public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
+ final Rect sensorBounds = new Rect(10, 10, 20, 20);
+ final int displayWidth = 1080;
+ final int displayHeight = 1920;
+ final float scaleFactor = 1f;
+ final int rotation = Surface.ROTATION_0;
+
+ // Initialize the overlay.
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ rotation));
+
+ // Show the overlay.
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).addView(any(), any());
+
+ // Update overlay with the same parameters.
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ rotation));
+ mFgExecutor.runAllReady();
+
+ // Ensure the overlay was not recreated.
+ verify(mWindowManager, never()).removeView(any());
+ }
+
+ private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
+ float major) {
+ MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
+ pp.id = 1;
+ MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
+ pc.x = x;
+ pc.y = y;
+ pc.touchMinor = minor;
+ pc.touchMajor = major;
+ return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
+ new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
+ }
+
+ @Test
+ public void onTouch_propagatesTouchInNativeOrientationAndResolution() throws RemoteException {
+ final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
+ final int displayWidth = 1080;
+ final int displayHeight = 1920;
+ final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560.
+ final float touchMinor = 10f;
+ final float touchMajor = 20f;
+
+ // Expecting a touch at the very bottom right corner in native orientation and resolution.
+ final int expectedX = (int) (displayWidth / scaleFactor);
+ final int expectedY = (int) (displayHeight / scaleFactor);
+ final float expectedMinor = touchMinor / scaleFactor;
+ final float expectedMajor = touchMajor / scaleFactor;
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // Show the overlay.
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // Test ROTATION_0
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ Surface.ROTATION_0));
+ MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
+ touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+
+ // Test ROTATION_90
+ reset(mAlternateTouchProvider);
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ Surface.ROTATION_90));
+ event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+
+ // Test ROTATION_270
+ reset(mAlternateTouchProvider);
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ Surface.ROTATION_270));
+ event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+
+ // Test ROTATION_180
+ reset(mAlternateTouchProvider);
+ mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
+ Surface.ROTATION_180));
+ // ROTATION_180 is not supported. It should be treated like ROTATION_0.
+ event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ }
+
+ @Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
@@ -415,15 +571,17 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+
+ // FIX THIS TEST
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
moveEvent.recycle();
// THEN FingerprintManager is notified about onPointerDown
- verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
- eq(0), eq(0), eq(0f), eq(0f));
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
+ eq(0f));
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat());
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
@@ -434,7 +592,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mOnIlluminatedRunnableCaptor.getValue().run();
InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
inOrder.verify(mFingerprintManager).onUiReady(
- eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorProps.sensorId));
+ eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorId));
inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
}
@@ -452,7 +610,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// AND onIlluminatedRunnable that notifies FingerprintManager is set
verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
mOnIlluminatedRunnableCaptor.getValue().run();
- verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat());
@@ -573,7 +731,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
downEvent.recycle();
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 6d4cc4c96e0b..744af589dfac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.LayoutInflater
+import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -69,9 +70,8 @@ class UdfpsViewTest : SysuiTestCase() {
com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController
- view.sensorProperties =
- SensorLocationInternal(DISPLAY_ID, SENSOR_X, SENSOR_Y, SENSOR_RADIUS)
- .asFingerprintSensorProperties()
+ val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
+ view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
view.setHbmProvider(hbmProvider)
ViewUtils.attachView(view)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index adb9c4d206e1..f93336134900 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -133,6 +133,22 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
}
+ @Test
+ fun testPhysicalPixelDisplaySizeChanged() {
+ setupResources(
+ roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px),
+ roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px))
+
+ roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
+ assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
+
+ roundedCornerResDelegate.physicalPixelDisplaySizeRatio = 0.5f
+
+ assertEquals(Size(2, 2), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize)
+ }
+
private fun getTestsDrawable(@DrawableRes drawableId: Int): Drawable? {
return mContext.createPackageContext("com.android.systemui.tests", 0)
.getDrawable(drawableId)
@@ -153,7 +169,6 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
res.addOverride(SystemUIR.array.config_roundedCornerDrawableArray, mockTypedArray)
res.addOverride(SystemUIR.array.config_roundedCornerTopDrawableArray, mockTypedArray)
res.addOverride(SystemUIR.array.config_roundedCornerBottomDrawableArray, mockTypedArray)
- res.addOverride(SystemUIR.array.config_roundedCornerMultipleRadiusArray, mockTypedArray)
res.addOverride(com.android.internal.R.dimen.rounded_corner_radius, radius ?: 0)
res.addOverride(com.android.internal.R.dimen.rounded_corner_radius_top, radiusTop ?: 0)
res.addOverride(com.android.internal.R.dimen.rounded_corner_radius_bottom,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index f567b55b7caa..9d4275e65302 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -41,7 +41,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.complication.DreamPreviewComplication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -100,9 +99,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
DreamOverlayStateController mStateController;
@Mock
- DreamPreviewComplication mPreviewComplication;
-
- @Mock
ViewGroup mDreamOverlayContainerViewParent;
@Mock
@@ -133,7 +129,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mDreamOverlayComponentFactory,
mStateController,
mKeyguardUpdateMonitor,
- mPreviewComplication,
mUiEventLogger);
}
@@ -209,31 +204,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
}
@Test
- public void testPreviewModeFalseByDefault() {
- mService.onBind(new Intent());
-
- assertThat(mService.isPreviewMode()).isFalse();
- }
-
- @Test
- public void testPreviewModeSetByIntentExtra() {
- final Intent intent = new Intent();
- intent.putExtra(DreamService.EXTRA_IS_PREVIEW, true);
- mService.onBind(intent);
-
- assertThat(mService.isPreviewMode()).isTrue();
- }
-
- @Test
- public void testDreamLabel() {
- final Intent intent = new Intent();
- intent.putExtra(DreamService.EXTRA_DREAM_LABEL, "TestDream");
- mService.onBind(intent);
-
- assertThat(mService.getDreamLabel()).isEqualTo("TestDream");
- }
-
- @Test
public void testDestroy() {
mService.onDestroy();
mMainExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 3ce9889571f1..fb64c7b58aac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -83,38 +83,6 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
@Test
- public void testStateChange_isPreviewMode() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController(
- mExecutor);
- stateController.addCallback(mCallback);
- stateController.setPreviewMode(true);
- mExecutor.runAllReady();
-
- verify(mCallback).onStateChanged();
- assertThat(stateController.isPreviewMode()).isTrue();
-
- Mockito.clearInvocations(mCallback);
- stateController.setPreviewMode(true);
- mExecutor.runAllReady();
- verify(mCallback, never()).onStateChanged();
- }
-
- @Test
- public void testPreviewModeFalseByDefault() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController(
- mExecutor);
- assertThat(stateController.isPreviewMode()).isFalse();
- }
-
- @Test
- public void testPreviewModeSetToTrue() {
- final DreamOverlayStateController stateController = new DreamOverlayStateController(
- mExecutor);
- stateController.setPreviewMode(true);
- assertThat(stateController.isPreviewMode()).isTrue();
- }
-
- @Test
public void testCallback() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5ed1d656a1f5..4d0feffa1d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -399,7 +399,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
/* resetLockoutRequiresHwToken */ false,
List.of(new SensorLocationInternal("" /* displayId */,
(int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
- when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
+ when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
return new Pair(radius, udfpsLocation);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 33db99342586..83fb82c1c493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -25,7 +25,6 @@ import org.mockito.Mockito.`when` as whenever
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
-import android.graphics.Color
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.GradientDrawable
@@ -35,7 +34,6 @@ import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
-import android.os.Handler
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -111,6 +109,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var gutsViewHolder: GutsViewHolder
@Mock private lateinit var viewHolder: MediaViewHolder
@Mock private lateinit var view: TransitionLayout
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@@ -145,8 +144,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var scrubbingElapsedTimeView: TextView
private lateinit var scrubbingTotalTimeView: TextView
private lateinit var actionsTopBarrier: Barrier
- @Mock private lateinit var longPressText: TextView
- @Mock private lateinit var handler: Handler
+ @Mock private lateinit var gutsText: TextView
@Mock private lateinit var mockAnimator: AnimatorSet
private lateinit var settings: ImageButton
private lateinit var cancel: View
@@ -228,6 +226,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
}
+ initGutsViewHolderMocks()
initMediaViewHolderMocks()
// Create media session
@@ -272,6 +271,20 @@ public class MediaControlPanelTest : SysuiTestCase() {
)
}
+ private fun initGutsViewHolderMocks() {
+ settings = ImageButton(context)
+ cancel = View(context)
+ cancelText = TextView(context)
+ dismiss = FrameLayout(context)
+ dismissText = TextView(context)
+ whenever(gutsViewHolder.gutsText).thenReturn(gutsText)
+ whenever(gutsViewHolder.settings).thenReturn(settings)
+ whenever(gutsViewHolder.cancel).thenReturn(cancel)
+ whenever(gutsViewHolder.cancelText).thenReturn(cancelText)
+ whenever(gutsViewHolder.dismiss).thenReturn(dismiss)
+ whenever(gutsViewHolder.dismissText).thenReturn(dismissText)
+ }
+
/**
* Initialize elements in media view holder
*/
@@ -291,12 +304,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
seamlessButton = View(context)
seamlessIcon = ImageView(context)
seamlessText = TextView(context)
- seekBar = SeekBar(context)
- settings = ImageButton(context)
- cancel = View(context)
- cancelText = TextView(context)
- dismiss = FrameLayout(context)
- dismissText = TextView(context)
+ seekBar = SeekBar(context).also { it.id = R.id.media_progress_bar }
action0 = ImageButton(context).also { it.setId(R.id.action0) }
action1 = ImageButton(context).also { it.setId(R.id.action1) }
@@ -341,6 +349,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView)
whenever(viewHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView)
+ whenever(viewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
+
// Transition View
whenever(view.parent).thenReturn(transitionParent)
whenever(view.rootView).thenReturn(transitionParent)
@@ -363,15 +373,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.action4).thenReturn(action4)
whenever(viewHolder.getAction(R.id.action4)).thenReturn(action4)
- // Long press menu
- whenever(viewHolder.longPressText).thenReturn(longPressText)
- whenever(longPressText.handler).thenReturn(handler)
- whenever(viewHolder.settings).thenReturn(settings)
- whenever(viewHolder.cancel).thenReturn(cancel)
- whenever(viewHolder.cancelText).thenReturn(cancelText)
- whenever(viewHolder.dismiss).thenReturn(dismiss)
- whenever(viewHolder.dismissText).thenReturn(dismissText)
-
whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
}
@@ -404,10 +405,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
listOf(recSubtitle1, recSubtitle2, recSubtitle3)
)
- // Long press menu
- whenever(recommendationViewHolder.settings).thenReturn(settings)
- whenever(recommendationViewHolder.cancel).thenReturn(cancel)
- whenever(recommendationViewHolder.dismiss).thenReturn(dismiss)
+ whenever(recommendationViewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
val actionIcon = Icon.createWithResource(context, R.drawable.ic_android)
whenever(smartspaceAction.icon).thenReturn(actionIcon)
@@ -539,8 +537,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun bind_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
- whenever(seekBarViewModel.getEnabled()).thenReturn(false)
+ fun bind_seekBarDisabled_hasActions_seekBarVisibilityIsSetToInvisible() {
+ useRealConstraintSets()
val icon = context.getDrawable(android.R.drawable.ic_media_play)
val semanticActions = MediaButton(
@@ -550,21 +548,84 @@ public class MediaControlPanelTest : SysuiTestCase() {
val state = mediaData.copy(semanticActions = semanticActions)
player.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
player.bindPlayer(state, PACKAGE)
- verify(expandedSet).setVisibility(R.id.media_progress_bar, ConstraintSet.INVISIBLE)
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.INVISIBLE)
}
@Test
fun bind_seekBarDisabled_noActions_seekBarVisibilityIsSetToGone() {
- whenever(seekBarViewModel.getEnabled()).thenReturn(false)
+ useRealConstraintSets()
+
+ val state = mediaData.copy(semanticActions = MediaButton())
+ player.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+ player.bindPlayer(state, PACKAGE)
+
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun bind_seekBarEnabled_seekBarVisible() {
+ useRealConstraintSets()
+
+ val state = mediaData.copy(semanticActions = MediaButton())
+ player.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+ player.bindPlayer(state, PACKAGE)
+
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.VISIBLE)
+ }
+
+ @Test
+ fun seekBarChangesToEnabledAfterBind_seekBarChangesToVisible() {
+ useRealConstraintSets()
+
+ val state = mediaData.copy(semanticActions = MediaButton())
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(state, PACKAGE)
+
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.VISIBLE)
+ }
+
+ @Test
+ fun seekBarChangesToDisabledAfterBind_noActions_seekBarChangesToGone() {
+ useRealConstraintSets()
val state = mediaData.copy(semanticActions = MediaButton())
player.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
player.bindPlayer(state, PACKAGE)
- verify(expandedSet).setVisibility(R.id.media_progress_bar, ConstraintSet.INVISIBLE)
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.GONE)
+ }
+
+ @Test
+ fun seekBarChangesToDisabledAfterBind_hasActions_seekBarChangesToInvisible() {
+ useRealConstraintSets()
+
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
+ val semanticActions = MediaButton(
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", null)
+ )
+ val state = mediaData.copy(semanticActions = semanticActions)
+
+ player.attachPlayer(viewHolder)
+ getEnabledChangeListener().onEnabledChanged(enabled = true)
+ player.bindPlayer(state, PACKAGE)
+
+ getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.INVISIBLE)
}
@Test
@@ -905,8 +966,10 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(seamless.isEnabled()).isFalse()
}
+ /* ***** Guts tests for the player ***** */
+
@Test
- fun longClick_gutsClosed() {
+ fun player_longClickWhenGutsClosed_gutsOpens() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
whenever(mediaViewController.isGutsVisible).thenReturn(false)
@@ -920,7 +983,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun longClick_gutsOpen() {
+ fun player_longClickWhenGutsOpen_gutsCloses() {
player.attachPlayer(viewHolder)
whenever(mediaViewController.isGutsVisible).thenReturn(true)
@@ -933,8 +996,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun cancelButtonClick_animation() {
+ fun player_cancelButtonClick_animation() {
player.attachPlayer(viewHolder)
+ player.bindPlayer(mediaData, KEY)
cancel.callOnClick()
@@ -942,7 +1006,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun settingsButtonClick() {
+ fun player_settingsButtonClick() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
@@ -956,7 +1020,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonClick() {
+ fun player_dismissButtonClick() {
val mediaKey = "key for dismissal"
player.attachPlayer(viewHolder)
val state = mediaData.copy(notificationKey = KEY)
@@ -969,7 +1033,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonDisabled() {
+ fun player_dismissButtonDisabled() {
val mediaKey = "key for dismissal"
player.attachPlayer(viewHolder)
val state = mediaData.copy(isClearable = false, notificationKey = KEY)
@@ -979,7 +1043,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun dismissButtonClick_notInManager() {
+ fun player_dismissButtonClick_notInManager() {
val mediaKey = "key for dismissal"
whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
@@ -994,6 +1058,76 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
}
+ /* ***** END guts tests for the player ***** */
+
+ /* ***** Guts tests for the recommendations ***** */
+
+ @Test
+ fun recommendations_longClickWhenGutsClosed_gutsOpens() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+ whenever(mediaViewController.isGutsVisible).thenReturn(false)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController).openGuts()
+ verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId))
+ }
+
+ @Test
+ fun recommendations_longClickWhenGutsOpen_gutsCloses() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+ whenever(mediaViewController.isGutsVisible).thenReturn(true)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController).closeGuts(false)
+ }
+
+ @Test
+ fun recommendations_cancelButtonClick_animation() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ cancel.callOnClick()
+
+ verify(mediaViewController).closeGuts(false)
+ }
+
+ @Test
+ fun recommendations_settingsButtonClick() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ settings.callOnClick()
+ verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId))
+
+ val captor = ArgumentCaptor.forClass(Intent::class.java)
+ verify(activityStarter).startActivity(captor.capture(), eq(true))
+
+ assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
+ }
+
+ @Test
+ fun recommendations_dismissButtonClick() {
+ val mediaKey = "key for dismissal"
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData.copy(targetId = mediaKey))
+
+ assertThat(dismiss.isEnabled).isEqualTo(true)
+ dismiss.callOnClick()
+ verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
+ verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong())
+ }
+
+ /* ***** END guts tests for the recommendations ***** */
+
@Test
fun actionPlayPauseClick_isLogged() {
val semanticActions = MediaButton(
@@ -1317,4 +1451,24 @@ public class MediaControlPanelTest : SysuiTestCase() {
private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
withArgCaptor { verify(seekBarViewModel).setScrubbingChangeListener(capture()) }
+
+ private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener =
+ withArgCaptor { verify(seekBarViewModel).setEnabledChangeListener(capture()) }
+
+ /**
+ * Update our test to use real ConstraintSets instead of mocks.
+ *
+ * Some item visibilities, such as the seekbar visibility, are dependent on other action's
+ * visibilities. If we use mocks for the ConstraintSets, then action visibility changes are
+ * just thrown away instead of being saved for reference later. This method sets us up to use
+ * ConstraintSets so that we do save visibility changes.
+ *
+ * TODO(b/229740380): Can/should we use real expanded and collapsed sets for all tests?
+ */
+ private fun useRealConstraintSets() {
+ expandedSet = ConstraintSet()
+ collapsedSet = ConstraintSet()
+ whenever(mediaViewController.expandedLayout).thenReturn(expandedSet)
+ whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
index b7d5ba170e6d..604e1f3ac9eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
@@ -12,6 +12,8 @@ import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
/**
* Tests for {@link MediaViewController}.
@@ -20,16 +22,25 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class MediaViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var logger: MediaViewLogger
+
private val configurationController =
com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context)
private val mediaHostStatesManager = MediaHostStatesManager()
- private val mediaViewController =
- MediaViewController(context, configurationController, mediaHostStatesManager)
+ private lateinit var mediaViewController: MediaViewController
private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mediaViewController = MediaViewController(
+ context,
+ configurationController,
+ mediaHostStatesManager,
+ logger
+ )
mediaViewController.attach(transitionLayout, MediaViewController.TYPE.PLAYER)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 380fa6d50df7..7c53388c81aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.media.dialog;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -26,18 +27,23 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -50,6 +56,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
@SmallTest
@@ -61,8 +69,14 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
// Mock
private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
+ private MediaController mMediaController = mock(MediaController.class);
+ private PlaybackState mPlaybackState = mock(PlaybackState.class);
private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+ LocalBluetoothProfileManager.class);
+ private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+ LocalBluetoothLeBroadcast.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
private BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
private NotificationEntryManager mNotificationEntryManager =
@@ -71,15 +85,26 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
NearbyMediaDevicesManager.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private List<MediaController> mMediaControllers = new ArrayList<>();
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
private int mHeaderIconRes;
private IconCompat mIconCompat;
private CharSequence mHeaderTitle;
private CharSequence mHeaderSubtitle;
+ private String mStopText;
+ private boolean mIsBroadcasting;
@Before
public void setUp() {
+ when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+ when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
+ when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+ mMediaControllers.add(mMediaController);
+ when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotificationEntryManager, mDialogLaunchAnimator,
@@ -173,6 +198,59 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
}
+ @Test
+ public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ mIsBroadcasting = true;
+
+ mMediaOutputBaseDialogImpl.onStart();
+
+ verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any());
+ }
+
+ @Test
+ public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ mIsBroadcasting = false;
+
+ mMediaOutputBaseDialogImpl.onStart();
+
+ verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
+ }
+
+ @Test
+ public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ mIsBroadcasting = true;
+
+ mMediaOutputBaseDialogImpl.onStop();
+
+ verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any());
+ }
+
+ @Test
+ public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ mIsBroadcasting = false;
+
+ mMediaOutputBaseDialogImpl.onStop();
+
+ verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any());
+ }
+
+ @Test
+ public void refresh_checkStopText() {
+ mStopText = "test_string";
+ mMediaOutputBaseDialogImpl.refresh();
+ final Button stop = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(R.id.stop);
+
+ assertThat(stop.getText().toString()).isEqualTo(mStopText);
+ }
+
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender,
@@ -216,5 +294,15 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
int getStopButtonVisibility() {
return 0;
}
+
+ @Override
+ public boolean isBroadcastSupported() {
+ return mIsBroadcasting;
+ }
+
+ @Override
+ public CharSequence getStopButtonText() {
+ return mStopText;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index db56f875dd5c..e6ad6edcec45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -18,13 +18,16 @@ package com.android.systemui.media.dialog;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.media.MediaRoute2Info;
+import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -32,7 +35,9 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
@@ -60,7 +65,13 @@ public class MediaOutputDialogTest extends SysuiTestCase {
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private MediaController mMediaController = mock(MediaController.class);
+ private PlaybackState mPlaybackState = mock(PlaybackState.class);
private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+ LocalBluetoothProfileManager.class);
+ private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+ LocalBluetoothLeBroadcast.class);
private final ActivityStarter mStarter = mock(ActivityStarter.class);
private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
@@ -72,12 +83,21 @@ public class MediaOutputDialogTest extends SysuiTestCase {
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
NearbyMediaDevicesManager.class);
+ private List<MediaController> mMediaControllers = new ArrayList<>();
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
private final List<String> mFeatures = new ArrayList<>();
@Before
public void setUp() {
+ when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+ when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
+ when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+ mMediaControllers.add(mMediaController);
+ when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotificationEntryManager, mDialogLaunchAnimator,
@@ -116,6 +136,13 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 4a740f6c5571..f5b006d732fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -205,9 +205,10 @@ public class NavigationBarTest extends SysuiTestCase {
when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton);
when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton);
when(mNavigationBarView.getBackButton()).thenReturn(mBackButton);
+ when(mNavigationBarView.getBarTransitions()).thenReturn(mNavigationBarTransitions);
when(mNavigationBarView.getRotationButtonController())
.thenReturn(mRotationButtonController);
- when(mNavigationBarTransitions.getLightTransitionsController())
+ when(mNavigationBarView.getLightTransitionsController())
.thenReturn(mLightBarTransitionsController);
when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
setupSysuiDependency();
@@ -458,7 +459,6 @@ public class NavigationBarTest extends SysuiTestCase {
mInputMethodManager,
mDeadZone,
mDeviceConfigProxyFake,
- mNavigationBarTransitions,
Optional.of(mock(BackAnimation.class))));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index 084eca82ce98..6a2a78b40d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -36,8 +37,8 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.BarTransitions;
-import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -52,10 +53,6 @@ import org.mockito.MockitoAnnotations;
public class NavigationBarTransitionsTest extends SysuiTestCase {
@Mock
- LightBarTransitionsController.Factory mLightBarTransitionsFactory;
- @Mock
- LightBarTransitionsController mLightBarTransitions;
- @Mock
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
@@ -79,11 +76,10 @@ public class NavigationBarTransitionsTest extends SysuiTestCase {
.when(mDependency.injectMockDependency(NavigationModeController.class))
.getCurrentUserContext();
- when(mLightBarTransitionsFactory.create(any())).thenReturn(mLightBarTransitions);
NavigationBarView navBar = spy(new NavigationBarView(mContext, null));
when(navBar.getCurrentView()).thenReturn(navBar);
when(navBar.findViewById(anyInt())).thenReturn(navBar);
- mTransitions = new NavigationBarTransitions(navBar, mLightBarTransitionsFactory);
+ mTransitions = new NavigationBarTransitions(navBar, mock(CommandQueue.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 1f90d0cf9494..87ca1aa5eeb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -47,7 +47,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
- fun testUpTranslationSetToDefaultValue() {
+ fun resetViewStates_defaultHun_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
@@ -57,7 +57,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
- fun testHeadsUpTranslationChangesBasedOnStackMargin() {
+ fun resetViewStates_stackMargin_changesHunYTranslation() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
val minHeadsUpTranslation = context.resources
@@ -72,7 +72,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
- fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() {
+ fun resetViewStates_emptyShadeView_isCenteredVertically() {
stackScrollAlgorithm.initView(context)
val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index c9903ea19868..e27b7a659ae6 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -19,6 +19,7 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Camera;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -752,13 +753,65 @@ public class CameraExtensionsProxyService extends Service {
public ISessionProcessorImpl getSessionProcessor() {
return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
}
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureRequestKeys(String cameraId) {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureRequest.Key> supportedCaptureKeys =
+ mAdvancedExtender.getAvailableCaptureRequestKeys();
+
+ if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
+ mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int requestKeyTags [] = new int[supportedCaptureKeys.size()];
+ int i = 0;
+ for (CaptureRequest.Key key : supportedCaptureKeys) {
+ requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureResultKeys(String cameraId) {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureResult.Key> supportedResultKeys =
+ mAdvancedExtender.getAvailableCaptureResultKeys();
+
+ if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
+ mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int resultKeyTags [] = new int[supportedResultKeys.size()];
+ int i = 0;
+ for (CaptureResult.Key key : supportedResultKeys) {
+ resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
}
private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
private final ICaptureCallback mCaptureCallback;
+ private final String mCameraId;
- private CaptureCallbackStub(ICaptureCallback captureCallback) {
+ private CaptureCallbackStub(ICaptureCallback captureCallback, String cameraId) {
mCaptureCallback = captureCallback;
+ mCameraId = cameraId;
}
@Override
@@ -820,6 +873,29 @@ public class CameraExtensionsProxyService extends Service {
}
}
}
+
+ @Override
+ public void onCaptureCompleted(long timestamp, int requestId,
+ Map<CaptureResult.Key, Object> result) {
+
+ if (result == null) {
+ Log.e(TAG, "Invalid capture result received!");
+ }
+
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ if (mMetadataVendorIdMap.containsKey(mCameraId)) {
+ captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId));
+ }
+ for (Map.Entry<CaptureResult.Key, Object> entry : result.entrySet()) {
+ captureResults.set(entry.getKey(), entry.getValue());
+ }
+
+ try {
+ mCaptureCallback.onCaptureCompleted(timestamp, requestId, captureResults);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture complete due to remote exception!");
+ }
+ }
}
private class RequestCallbackStub extends IRequestCallback.Stub {
@@ -1124,7 +1200,7 @@ public class CameraExtensionsProxyService extends Service {
@Override
public int startRepeating(ICaptureCallback callback) {
- return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
+ return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback, mCameraId));
}
@Override
@@ -1133,12 +1209,29 @@ public class CameraExtensionsProxyService extends Service {
}
@Override
- public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
+ public void setParameters(CaptureRequest captureRequest) {
HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
- paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
- paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ for (CaptureRequest.Key captureRequestKey : captureRequest.getKeys()) {
+ paramMap.put(captureRequestKey, captureRequest.get(captureRequestKey));
+ }
+
mSessionProcessor.setParameters(paramMap);
- return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
+ }
+
+ @Override
+ public int startTrigger(CaptureRequest captureRequest, ICaptureCallback callback) {
+ HashMap<CaptureRequest.Key<?>, Object> triggerMap = new HashMap<>();
+ for (CaptureRequest.Key captureRequestKey : captureRequest.getKeys()) {
+ triggerMap.put(captureRequestKey, captureRequest.get(captureRequestKey));
+ }
+
+ return mSessionProcessor.startTrigger(triggerMap,
+ new CaptureCallbackStub(callback, mCameraId));
+ }
+
+ @Override
+ public int startCapture(ICaptureCallback callback) {
+ return mSessionProcessor.startCapture(new CaptureCallbackStub(callback, mCameraId));
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 562d11a0b197..649328d1eef3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -84,16 +84,15 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.IInputSessionWithIdCallback;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
@@ -1655,21 +1654,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mInvocationHandler.createImeSessionLocked();
}
- public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
+ boolean enabled) {
mInvocationHandler.setImeSessionEnabledLocked(session, enabled);
}
- public void bindInputLocked(InputBinding binding) {
- mInvocationHandler.bindInputLocked(binding);
+ public void bindInputLocked() {
+ mInvocationHandler.bindInputLocked();
}
public void unbindInputLocked() {
mInvocationHandler.unbindInputLocked();
}
- public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ public void startInputLocked(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
- mInvocationHandler.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ mInvocationHandler.startInputLocked(connection, editorInfo, restarting);
}
@@ -1827,7 +1827,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void setImeSessionEnabledInternal(IInputMethodSession session, boolean enabled) {
+ private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
+ boolean enabled) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null && session != null) {
try {
@@ -1842,14 +1843,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void bindInputInternal(InputBinding binding) {
+ private void bindInputInternal() {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
- logTraceSvcClient("bindInput", binding.toString());
+ logTraceSvcClient("bindInput", "");
}
- listener.bindInput(binding);
+ listener.bindInput();
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error binding input to " + mService, re);
@@ -1872,16 +1873,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void startInputInternal(IBinder startInputToken, IInputContext inputContext,
+ private void startInputInternal(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
- logTraceSvcClient("startInput", startInputToken + " "
- + inputContext + " " + editorInfo + restarting);
+ logTraceSvcClient("startInput", "editorInfo=" + editorInfo
+ + " restarting=" + restarting);
}
- listener.startInput(startInputToken, inputContext, editorInfo, restarting);
+ listener.startInput(connection, editorInfo, restarting);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
"Error starting input to " + mService, re);
@@ -2141,12 +2142,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
break;
case MSG_SET_IME_SESSION_ENABLED:
final boolean enabled = (message.arg1 != 0);
- final IInputMethodSession session = (IInputMethodSession) message.obj;
+ final IAccessibilityInputMethodSession session =
+ (IAccessibilityInputMethodSession) message.obj;
setImeSessionEnabledInternal(session, enabled);
break;
case MSG_BIND_INPUT:
- final InputBinding binding = (InputBinding) message.obj;
- bindInputInternal(binding);
+ bindInputInternal();
break;
case MSG_UNBIND_INPUT:
unbindInputInternal();
@@ -2154,10 +2155,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
case MSG_START_INPUT:
final boolean restarting = (message.arg1 != 0);
final SomeArgs args = (SomeArgs) message.obj;
- final IBinder startInputToken = (IBinder) args.arg1;
- final IInputContext inputContext = (IInputContext) args.arg2;
- final EditorInfo editorInfo = (EditorInfo) args.arg3;
- startInputInternal(startInputToken, inputContext, editorInfo, restarting);
+ final IRemoteAccessibilityInputConnection connection =
+ (IRemoteAccessibilityInputConnection) args.arg1;
+ final EditorInfo editorInfo = (EditorInfo) args.arg2;
+ startInputInternal(connection, editorInfo, restarting);
+ args.recycle();
break;
default: {
throw new IllegalArgumentException("Unknown message: " + type);
@@ -2227,14 +2229,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
msg.sendToTarget();
}
- public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ public void setImeSessionEnabledLocked(IAccessibilityInputMethodSession session,
+ boolean enabled) {
final Message msg = obtainMessage(MSG_SET_IME_SESSION_ENABLED, (enabled ? 1 : 0),
0, session);
msg.sendToTarget();
}
- public void bindInputLocked(InputBinding binding) {
- final Message msg = obtainMessage(MSG_BIND_INPUT, binding);
+ public void bindInputLocked() {
+ final Message msg = obtainMessage(MSG_BIND_INPUT);
msg.sendToTarget();
}
@@ -2243,12 +2246,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
msg.sendToTarget();
}
- public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ public void startInputLocked(
+ IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = startInputToken;
- args.arg2 = inputContext;
- args.arg3 = editorInfo;
+ args.arg1 = connection;
+ args.arg2 = editorInfo;
final Message msg = obtainMessage(MSG_START_INPUT, restarting ? 1 : 0, 0, args);
msg.sendToTarget();
}
@@ -2402,10 +2405,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private static final class AccessibilityCallback extends IInputSessionWithIdCallback.Stub {
+ private static final class AccessibilityCallback
+ extends IAccessibilityInputMethodSessionCallback.Stub {
@Override
- public void sessionCreated(IInputMethodSession session, int id) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated");
+ public void sessionCreated(IAccessibilityInputMethodSession session, int id) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AACS.sessionCreated");
final long ident = Binder.clearCallingIdentity();
try {
InputMethodManagerInternal.get().onSessionForAccessibilityCreated(id, session);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ac0c051794b3..cbeb01a68778 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -118,7 +118,6 @@ import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -128,12 +127,12 @@ import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserAct
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -281,9 +280,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
- private InputBinding mInputBinding;
- IBinder mStartInputToken;
- IInputContext mInputContext;
+ private boolean mInputBound;
+ IRemoteAccessibilityInputConnection mRemoteInputConnection;
EditorInfo mEditorInfo;
boolean mRestarting;
boolean mInputSessionRequested;
@@ -322,7 +320,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ public void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
boolean enabled) {
mService.scheduleSetImeSessionEnabled(sessions, enabled);
}
@@ -333,8 +331,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void bindInput(InputBinding binding) {
- mService.scheduleBindInput(binding);
+ public void bindInput() {
+ mService.scheduleBindInput();
}
@Override
@@ -343,9 +341,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
EditorInfo editorInfo, boolean restarting) {
- mService.scheduleStartInput(startInputToken, inputContext, editorInfo, restarting);
+ mService.scheduleStartInput(remoteAccessibilityInputConnection, editorInfo, restarting);
}
}
@@ -4350,10 +4349,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void bindAndStartInputForConnection(AbstractAccessibilityServiceConnection connection) {
synchronized (mLock) {
- if (mInputBinding != null) {
- connection.bindInputLocked(mInputBinding);
- connection.startInputLocked(mStartInputToken, mInputContext, mEditorInfo,
- mRestarting);
+ if (mInputBound) {
+ connection.bindInputLocked();
+ connection.startInputLocked(mRemoteInputConnection, mEditorInfo, mRestarting);
}
}
}
@@ -4396,23 +4394,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Bind input for accessibility services which request ime capabilities.
- *
- * @param binding Information given to an accessibility service about a client connecting to it.
*/
- public void scheduleBindInput(InputBinding binding) {
- mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this,
- binding));
+ public void scheduleBindInput() {
+ mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this));
}
- private void bindInput(InputBinding binding) {
+ private void bindInput() {
synchronized (mLock) {
// Keep records of these in case new Accessibility Services are enabled.
- mInputBinding = binding;
+ mInputBound = true;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.requestImeApis()) {
- service.bindInputLocked(binding);
+ service.bindInputLocked();
}
}
}
@@ -4427,6 +4422,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void unbindInput() {
synchronized (mLock) {
+ mInputBound = false;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -4440,25 +4436,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Start input for accessibility services which request ime capabilities.
*/
- public void scheduleStartInput(IBinder startInputToken, IInputContext inputContext,
+ public void scheduleStartInput(IRemoteAccessibilityInputConnection connection,
EditorInfo editorInfo, boolean restarting) {
mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::startInput, this,
- startInputToken, inputContext, editorInfo, restarting));
+ connection, editorInfo, restarting));
}
- private void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo editorInfo, boolean restarting) {
+ private void startInput(IRemoteAccessibilityInputConnection connection, EditorInfo editorInfo,
+ boolean restarting) {
synchronized (mLock) {
// Keep records of these in case new Accessibility Services are enabled.
- mStartInputToken = startInputToken;
- mInputContext = inputContext;
+ mRemoteInputConnection = connection;
mEditorInfo = editorInfo;
mRestarting = restarting;
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.requestImeApis()) {
- service.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ service.startInputLocked(connection, editorInfo, restarting);
}
}
}
@@ -4492,13 +4487,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param sessions Sessions to enable or disable.
* @param enabled True if enable the sessions or false if disable the sessions.
*/
- public void scheduleSetImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ public void scheduleSetImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
boolean enabled) {
mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::setImeSessionEnabled,
this, sessions, enabled));
}
- private void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) {
+ private void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
+ boolean enabled) {
synchronized (mLock) {
AccessibilityUserState userState = getCurrentUserStateLocked();
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
index eb16d3bdbd1b..d1c0482b4be6 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
@@ -35,6 +35,7 @@ public class RemoteCloudSearchService extends
private static final String TAG = "RemoteCloudSearchService";
+ private static final long TIMEOUT_IDLE_BOUND_TIMEOUT_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteCloudSearchServiceCallbacks mCallback;
@@ -57,7 +58,7 @@ public class RemoteCloudSearchService extends
@Override
protected long getTimeoutIdleBindMillis() {
- return PERMANENT_BOUND_TIMEOUT_MS;
+ return TIMEOUT_IDLE_BOUND_TIMEOUT_MS;
}
@Override
diff --git a/services/core/java/com/android/server/AccessibilityManagerInternal.java b/services/core/java/com/android/server/AccessibilityManagerInternal.java
index 28f6db1c800b..6ca32af8c3cb 100644
--- a/services/core/java/com/android/server/AccessibilityManagerInternal.java
+++ b/services/core/java/com/android/server/AccessibilityManagerInternal.java
@@ -17,28 +17,26 @@
package com.android.server;
import android.annotation.NonNull;
-import android.os.IBinder;
import android.util.ArraySet;
import android.util.SparseArray;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
/**
* Accessibility manager local system service interface.
*/
public abstract class AccessibilityManagerInternal {
/** Enable or disable the sessions. */
- public abstract void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
- boolean enabled);
+ public abstract void setImeSessionEnabled(
+ SparseArray<IAccessibilityInputMethodSession> sessions, boolean enabled);
/** Unbind input for all accessibility services which require ime capabilities. */
public abstract void unbindInput();
/** Bind input for all accessibility services which require ime capabilities. */
- public abstract void bindInput(InputBinding binding);
+ public abstract void bindInput();
/**
* Request input session from all accessibility services which require ime capabilities and
@@ -47,12 +45,13 @@ public abstract class AccessibilityManagerInternal {
public abstract void createImeSession(ArraySet<Integer> ignoreSet);
/** Start input for all accessibility services which require ime capabilities. */
- public abstract void startInput(IBinder startInputToken, IInputContext inputContext,
+ public abstract void startInput(
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
EditorInfo editorInfo, boolean restarting);
private static final AccessibilityManagerInternal NOP = new AccessibilityManagerInternal() {
@Override
- public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ public void setImeSessionEnabled(SparseArray<IAccessibilityInputMethodSession> sessions,
boolean enabled) {
}
@@ -61,7 +60,7 @@ public abstract class AccessibilityManagerInternal {
}
@Override
- public void bindInput(InputBinding binding) {
+ public void bindInput() {
}
@Override
@@ -69,7 +68,7 @@ public abstract class AccessibilityManagerInternal {
}
@Override
- public void startInput(IBinder startInputToken, IInputContext inputContext,
+ public void startInput(IRemoteAccessibilityInputConnection remoteAccessibility,
EditorInfo editorInfo, boolean restarting) {
}
};
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 991c7a9e0a4d..8f37823e4f9a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3742,6 +3742,16 @@ class StorageManagerService extends IStorageManager.Stub
Context.APP_OPS_SERVICE);
appOps.checkPackage(callingUid, callingPkg);
+ try {
+ final PackageManager.Property noAppStorageProp = mContext.getPackageManager()
+ .getProperty(PackageManager.PROPERTY_NO_APP_DATA_STORAGE, callingPkg);
+ if (noAppStorageProp != null && noAppStorageProp.getBoolean()) {
+ throw new SecurityException(callingPkg + " should not have " + appPath);
+ }
+ } catch (PackageManager.NameNotFoundException ignore) {
+ // Property not found
+ }
+
File appFile = null;
try {
appFile = new File(appPath).getCanonicalFile();
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 8c42d4d1bd5a..6fa13eb0058b 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -269,10 +269,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN, // RestrictionLevel
AppBackgroundRestrictionsInfo.THRESHOLD_UNKNOWN,
AppBackgroundRestrictionsInfo.UNKNOWN_TRACKER,
- null /*byte[] fgs_tracker_info*/,
- getBatteryTrackerInfoProtoLocked(uid) /*byte[] battery_tracker_info*/,
- null /*byte[] broadcast_events_tracker_info*/,
- null /*byte[] bind_service_events_tracker_info*/,
+ null, // FgsTrackerInfo
+ getTrackerInfoForStatsd(uid),
+ null, // BroadcastEventsTrackerInfo
+ null, // BindServiceEventsTrackerInfo
AppBackgroundRestrictionsInfo.REASON_UNKNOWN, // ExemptionReason
AppBackgroundRestrictionsInfo.UNKNOWN, // OptimizationLevel
AppBackgroundRestrictionsInfo.SDK_UNKNOWN, // TargetSdk
@@ -282,14 +282,14 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
}
/**
- * Get the BatteryTrackerInfo proto of a UID.
- * @param uid
- * @return byte array of the proto.
+ * Get the BatteryTrackerInfo object of the given uid.
+ * @return byte array of the proto object.
*/
- @NonNull byte[] getBatteryTrackerInfoProtoLocked(int uid) {
+ @Override
+ byte[] getTrackerInfoForStatsd(int uid) {
final ImmutableBatteryUsage temp = mUidBatteryUsageInWindow.get(uid);
if (temp == null) {
- return new byte[0];
+ return null;
}
final BatteryUsage bgUsage = temp.calcPercentage(uid, mInjector.getPolicy());
final double allUsage = bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_UNSPECIFIED]
@@ -301,10 +301,12 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND];
final double usageFgs =
bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE];
- Slog.d(TAG, "getBatteryTrackerInfoProtoLocked uid:" + uid
- + " allUsage:" + String.format("%4.2f%%", allUsage)
- + " usageBackground:" + String.format("%4.2f%%", usageBackground)
- + " usageFgs:" + String.format("%4.2f%%", usageFgs));
+ if (DEBUG_BACKGROUND_BATTERY_TRACKER_VERBOSE) {
+ Slog.d(TAG, "getBatteryTrackerInfoProtoLocked uid:" + uid
+ + " allUsage:" + String.format("%4.2f%%", allUsage)
+ + " usageBackground:" + String.format("%4.2f%%", usageBackground)
+ + " usageFgs:" + String.format("%4.2f%%", usageFgs));
+ }
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_24H,
allUsage * 10000);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 97dd3230176e..36c40a1b97dc 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -577,12 +577,14 @@ class AppErrors {
mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
PackageWatchdog.FAILURE_REASON_APP_CRASH);
- mService.mProcessList.noteAppKill(r, (crashInfo != null
- && "Native crash".equals(crashInfo.exceptionClassName))
- ? ApplicationExitInfo.REASON_CRASH_NATIVE
- : ApplicationExitInfo.REASON_CRASH,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "crash");
+ synchronized (mService) {
+ mService.mProcessList.noteAppKill(r, (crashInfo != null
+ && "Native crash".equals(crashInfo.exceptionClassName))
+ ? ApplicationExitInfo.REASON_CRASH_NATIVE
+ : ApplicationExitInfo.REASON_CRASH,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "crash");
+ }
}
final int relaunchReason = r != null
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 41c741c8cd77..da083aff62ff 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -330,6 +330,8 @@ public final class AppRestrictionController {
})
@interface TrackerType {}
+ private final TrackerInfo mEmptyTrackerInfo = new TrackerInfo();
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -916,7 +918,7 @@ public final class AppRestrictionController {
final int curBucket = mInjector.getAppStandbyInternal().getAppStandbyBucket(
packageName, UserHandle.getUserId(uid), now, false);
if (applyLevel) {
- applyRestrictionLevel(packageName, uid, curLevel, TRACKER_TYPE_UNKNOWN,
+ applyRestrictionLevel(packageName, uid, curLevel, mEmptyTrackerInfo,
curBucket, true, reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
} else {
pkgSettings.update(curLevel,
@@ -1333,6 +1335,25 @@ public final class AppRestrictionController {
}
}
+ /**
+ * A helper object which holds an app state tracker's type and its relevant info used for
+ * logging atoms to statsd.
+ */
+ private class TrackerInfo {
+ final int mType; // tracker type
+ final byte[] mInfo; // tracker info proto object for statsd
+
+ TrackerInfo() {
+ mType = TRACKER_TYPE_UNKNOWN;
+ mInfo = null;
+ }
+
+ TrackerInfo(int type, byte[] info) {
+ mType = type;
+ mInfo = info;
+ }
+ }
+
private final ConstantsObserver mConstantsObserver;
private final AppStateTracker.BackgroundRestrictedAppListener mBackgroundRestrictionListener =
@@ -1580,7 +1601,7 @@ public final class AppRestrictionController {
Slog.e(TAG, "Unable to find " + info.mPackageName + "/u" + userId);
continue;
}
- final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
+ final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
userId, uid, info.mPackageName, info.mStandbyBucket, false, false);
if (DEBUG_BG_RESTRICTION_CONTROLLER) {
Slog.i(TAG, "Proposed restriction level of " + info.mPackageName + "/"
@@ -1604,8 +1625,8 @@ public final class AppRestrictionController {
final long now = SystemClock.elapsedRealtime();
for (String pkg: packages) {
final int curBucket = appStandbyInternal.getAppStandbyBucket(pkg, userId, now, false);
- final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(userId, uid, pkg,
- curBucket, allowRequestBgRestricted, true);
+ final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(userId, uid,
+ pkg, curBucket, allowRequestBgRestricted, true);
if (DEBUG_BG_RESTRICTION_CONTROLLER) {
Slog.i(TAG, "Proposed restriction level of " + pkg + "/"
+ UserHandle.formatUid(uid) + ": "
@@ -1616,14 +1637,14 @@ public final class AppRestrictionController {
}
}
- private Pair<Integer, Integer> calcAppRestrictionLevel(@UserIdInt int userId, int uid,
+ private Pair<Integer, TrackerInfo> calcAppRestrictionLevel(@UserIdInt int userId, int uid,
String packageName, @UsageStatsManager.StandbyBuckets int standbyBucket,
boolean allowRequestBgRestricted, boolean calcTrackers) {
if (mInjector.getAppHibernationInternal().isHibernatingForUser(packageName, userId)) {
- return new Pair<>(RESTRICTION_LEVEL_HIBERNATION, TRACKER_TYPE_UNKNOWN);
+ return new Pair<>(RESTRICTION_LEVEL_HIBERNATION, mEmptyTrackerInfo);
}
@RestrictionLevel int level;
- @TrackerType int trackerType = TRACKER_TYPE_UNKNOWN;
+ TrackerInfo trackerInfo = null;
switch (standbyBucket) {
case STANDBY_BUCKET_EXEMPTED:
level = RESTRICTION_LEVEL_EXEMPTED;
@@ -1639,22 +1660,22 @@ public final class AppRestrictionController {
default:
if (mInjector.getAppStateTracker()
.isAppBackgroundRestricted(uid, packageName)) {
- return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, trackerType);
+ return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, mEmptyTrackerInfo);
}
level = mConstantsObserver.mRestrictedBucketEnabled
&& standbyBucket == STANDBY_BUCKET_RESTRICTED
? RESTRICTION_LEVEL_RESTRICTED_BUCKET
: RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
if (calcTrackers) {
- Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevelFromTackers(
+ Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevelFromTackers(
uid, packageName, RESTRICTION_LEVEL_MAX);
@RestrictionLevel int l = levelTypePair.first;
if (l == RESTRICTION_LEVEL_EXEMPTED) {
return new Pair<>(RESTRICTION_LEVEL_EXEMPTED, levelTypePair.second);
}
- level = Math.max(l, level);
- if (l == level) {
- trackerType = levelTypePair.second;
+ if (l > level) {
+ level = l;
+ trackerInfo = levelTypePair.second;
}
if (level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
// This level can't be entered without user consent
@@ -1666,29 +1687,29 @@ public final class AppRestrictionController {
levelTypePair = calcAppRestrictionLevelFromTackers(uid, packageName,
RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
level = levelTypePair.first;
- trackerType = levelTypePair.second;
+ trackerInfo = levelTypePair.second;
}
}
break;
}
- return new Pair<>(level, trackerType);
+ return new Pair<>(level, trackerInfo);
}
/**
* Ask each of the trackers for their proposed restriction levels for the given uid/package,
- * and return the most restrictive level along with the type of tracker which applied this
- * restriction level as a {@code Pair<@RestrictionLevel, @TrackerType>}.
+ * and return the most restrictive level along with the type of tracker and its relevant info
+ * which applied this restriction level as a {@code Pair<@RestrictionLevel, TrackerInfo>}.
*
* <p>Note, it's different from the {@link #getRestrictionLevel} where it returns the least
* restrictive level. We're returning the most restrictive level here because each tracker
* monitors certain dimensions of the app, the abusive behaviors could be detected in one or
* more of these dimensions, but not necessarily all of them. </p>
*/
- private Pair<Integer, Integer> calcAppRestrictionLevelFromTackers(int uid, String packageName,
- @RestrictionLevel int maxLevel) {
+ private Pair<Integer, TrackerInfo> calcAppRestrictionLevelFromTackers(int uid,
+ String packageName, @RestrictionLevel int maxLevel) {
@RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
@RestrictionLevel int prevLevel = level;
- @TrackerType int trackerType = TRACKER_TYPE_UNKNOWN;
+ BaseAppStateTracker resultTracker = null;
final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
@RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
@@ -1698,11 +1719,15 @@ public final class AppRestrictionController {
}
level = Math.max(level, l);
if (level != prevLevel) {
- trackerType = mAppStateTrackers.get(i).getType();
+ resultTracker = mAppStateTrackers.get(i);
prevLevel = level;
}
}
- return new Pair<>(level, trackerType);
+ final TrackerInfo trackerInfo = resultTracker == null
+ ? mEmptyTrackerInfo
+ : new TrackerInfo(resultTracker.getType(),
+ resultTracker.getTrackerInfoForStatsd(uid));
+ return new Pair<>(level, trackerInfo);
}
private static @RestrictionLevel int standbyBucketToRestrictionLevel(
@@ -2019,7 +2044,7 @@ public final class AppRestrictionController {
}
private void applyRestrictionLevel(String pkgName, int uid,
- @RestrictionLevel int level, @TrackerType int trackerType,
+ @RestrictionLevel int level, TrackerInfo trackerInfo,
int curBucket, boolean allowUpdateBucket, int reason, int subReason) {
int curLevel;
int prevReason;
@@ -2100,14 +2125,17 @@ public final class AppRestrictionController {
reason, subReason);
}
+ if (trackerInfo == null) {
+ trackerInfo = mEmptyTrackerInfo;
+ }
FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO, uid,
getRestrictionLevelStatsd(level),
getThresholdStatsd(reason),
- getTrackerTypeStatsd(trackerType),
- null, // FgsTrackerInfo
- null, // BatteryTrackerInfo
- null, // BroadcastEventsTrackerInfo
- null, // BindServiceEventsTrackerInfo
+ getTrackerTypeStatsd(trackerInfo.mType),
+ trackerInfo.mType == TRACKER_TYPE_FGS ? trackerInfo.mInfo : null,
+ trackerInfo.mType == TRACKER_TYPE_BATTERY ? trackerInfo.mInfo : null,
+ trackerInfo.mType == TRACKER_TYPE_BROADCAST_EVENTS ? trackerInfo.mInfo : null,
+ trackerInfo.mType == TRACKER_TYPE_BIND_SERVICE_EVENTS ? trackerInfo.mInfo : null,
getExemptionReasonStatsd(uid, level),
getOptimizationLevelStatsd(level),
getTargetSdkStatsd(pkgName),
@@ -2129,7 +2157,7 @@ public final class AppRestrictionController {
// The app could fall into the background restricted with user consent only,
// so set the reason to it.
applyRestrictionLevel(pkgName, uid, RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
- TRACKER_TYPE_UNKNOWN, curBucket, true, REASON_MAIN_FORCED_BY_USER,
+ mEmptyTrackerInfo, curBucket, true, REASON_MAIN_FORCED_BY_USER,
REASON_SUB_FORCED_USER_FLAG_INTERACTION);
mBgHandler.obtainMessage(BgHandler.MSG_CANCEL_REQUEST_BG_RESTRICTED, uid, 0, pkgName)
.sendToTarget();
@@ -2142,7 +2170,7 @@ public final class AppRestrictionController {
? STANDBY_BUCKET_EXEMPTED
: (lastLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE);
- final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
+ final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
UserHandle.getUserId(uid), uid, pkgName, tentativeBucket, false, true);
applyRestrictionLevel(pkgName, uid, levelTypePair.first, levelTypePair.second,
@@ -2186,7 +2214,7 @@ public final class AppRestrictionController {
@UserIdInt int userId) {
final int uid = mInjector.getPackageManagerInternal().getPackageUid(
packageName, STOCK_PM_FLAGS, userId);
- final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
+ final Pair<Integer, TrackerInfo> levelTypePair = calcAppRestrictionLevel(
userId, uid, packageName, bucket, false, false);
applyRestrictionLevel(packageName, uid, levelTypePair.first, levelTypePair.second,
bucket, false, REASON_MAIN_DEFAULT, REASON_SUB_DEFAULT_UNDEFINED);
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 5afcecaa069e..8d609104656d 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -169,6 +169,13 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
}
/**
+ * Return the relevant info object for the tracker for the given uid, used for statsd.
+ */
+ byte[] getTrackerInfoForStatsd(int uid) {
+ return null;
+ }
+
+ /**
* Return the policy holder of this tracker.
*/
T getPolicy() {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1a482e47e5c3..0b3c18b5113b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -656,6 +656,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET;
}
break;
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ audioDevice = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
+ break;
default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile());
}
return new BtDeviceInfo(d, device, state, audioDevice, codec);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 69b75e6e8edb..e14527098a72 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -370,6 +370,7 @@ public class AudioDeviceInventory {
}
break;
case BluetoothProfile.LE_AUDIO:
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
if (switchToUnavailable) {
makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
} else if (switchToAvailable) {
@@ -847,7 +848,10 @@ public class AudioDeviceInventory {
disconnectHearingAid();
break;
case BluetoothProfile.LE_AUDIO:
- disconnectLeAudio();
+ disconnectLeAudioUnicast();
+ break;
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ disconnectLeAudioBroadcast();
break;
default:
// Not a valid profile to disconnect
@@ -857,28 +861,39 @@ public class AudioDeviceInventory {
}
}
- /*package*/ void disconnectLeAudio() {
+ /*package*/ void disconnectLeAudio(int device) {
+ if (device != AudioSystem.DEVICE_OUT_BLE_HEADSET ||
+ device != AudioSystem.DEVICE_OUT_BLE_BROADCAST) {
+ Log.e(TAG, "disconnectLeAudio: Can't disconnect not LE Audio device " + device);
+ return;
+ }
+
synchronized (mDevicesLock) {
final ArraySet<String> toRemove = new ArraySet<>();
- // Disconnect ALL DEVICE_OUT_BLE_HEADSET devices
+ // Disconnect ALL DEVICE_OUT_BLE_HEADSET or DEVICE_OUT_BLE_BROADCAST devices
mConnectedDevices.values().forEach(deviceInfo -> {
- if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) {
+ if (deviceInfo.mDeviceType == device) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(
- AudioSystem.DEVICE_OUT_BLE_HEADSET, 0, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
- makeLeAudioDeviceUnavailable(deviceAddress,
- AudioSystem.DEVICE_OUT_BLE_HEADSET)
+ makeLeAudioDeviceUnavailable(deviceAddress, device)
);
}
}
}
+ /*package*/ void disconnectLeAudioUnicast() {
+ disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ /*package*/ void disconnectLeAudioBroadcast() {
+ disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
+ }
+
// must be called before removing the device from mConnectedDevices
// musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
// from AudioSystem
@@ -908,7 +923,9 @@ public class AudioDeviceInventory {
int delay;
synchronized (mDevicesLock) {
if (!info.mSupprNoisy
- && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput)
+ && (((info.mProfile == BluetoothProfile.LE_AUDIO
+ || info.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST)
+ && info.mIsLeOutput)
|| info.mProfile == BluetoothProfile.HEARING_AID
|| info.mProfile == BluetoothProfile.A2DP)) {
@AudioService.ConnectionState int asState =
@@ -1162,8 +1179,7 @@ public class AudioDeviceInventory {
return;
}
- final int leAudioVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ final int leAudioVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, device);
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
@@ -1215,6 +1231,7 @@ public class AudioDeviceInventory {
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b1b5d3ffb2c7..1357ed244d10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3228,7 +3228,8 @@ public class AudioService extends IAudioService.Stub
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
- if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -3891,7 +3892,8 @@ public class AudioService extends IAudioService.Stub
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
- if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+ || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -6832,6 +6834,7 @@ public class AudioService extends IAudioService.Stub
BluetoothProfile.A2DP,
BluetoothProfile.A2DP_SINK,
BluetoothProfile.LE_AUDIO,
+ BluetoothProfile.LE_AUDIO_BROADCAST,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BtProfile {}
@@ -6853,6 +6856,7 @@ public class AudioService extends IAudioService.Stub
final int profile = info.getProfile();
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
&& profile != BluetoothProfile.LE_AUDIO
+ && profile != BluetoothProfile.LE_AUDIO_BROADCAST
&& profile != BluetoothProfile.HEARING_AID) {
throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
+ previousDevice + " -> " + newDevice + ". Got: " + profile);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 54e83ec5303a..4bba68671968 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
+import com.android.server.display.DisplayPowerController.BrightnessEvent;
import java.io.PrintWriter;
@@ -147,6 +148,9 @@ class AutomaticBrightnessController {
// The currently accepted nominal ambient light level.
private float mAmbientLux;
+ // The last ambient lux value prior to passing the darkening or brightening threshold.
+ private float mPreThresholdLux;
+
// True if mAmbientLux holds a valid value.
private boolean mAmbientLuxValid;
@@ -154,6 +158,9 @@ class AutomaticBrightnessController {
private float mAmbientBrighteningThreshold;
private float mAmbientDarkeningThreshold;
+ // The last brightness value prior to passing the darkening or brightening threshold.
+ private float mPreThresholdBrightness;
+
// The screen brightness threshold at which to brighten or darken the screen.
private float mScreenBrighteningThreshold;
private float mScreenDarkeningThreshold;
@@ -325,6 +332,21 @@ class AutomaticBrightnessController {
}
public float getAutomaticScreenBrightness() {
+ return getAutomaticScreenBrightness(null);
+ }
+
+ float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
+ if (brightnessEvent != null) {
+ brightnessEvent.lux =
+ mAmbientLuxValid ? mAmbientLux : PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ brightnessEvent.preThresholdLux = mPreThresholdLux;
+ brightnessEvent.preThresholdBrightness = mPreThresholdBrightness;
+ brightnessEvent.recommendedBrightness = mScreenAutoBrightness;
+ brightnessEvent.flags |= (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
+ | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
+ ? BrightnessEvent.FLAG_DOZE_SCALE : 0);
+ }
+
if (!mAmbientLuxValid) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
@@ -506,6 +528,8 @@ class AutomaticBrightnessController {
pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
+ pw.println(" mPreThesholdLux=" + mPreThresholdLux);
+ pw.println(" mPreThesholdBrightness=" + mPreThresholdBrightness);
pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
@@ -574,7 +598,11 @@ class AutomaticBrightnessController {
} else if (mLightSensorEnabled) {
mLightSensorEnabled = false;
mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
+ if (!mAmbientLuxValid) {
+ mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mRecentLightSamples = 0;
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
@@ -790,6 +818,7 @@ class AutomaticBrightnessController {
|| (slowAmbientLux <= mAmbientDarkeningThreshold
&& fastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
+ mPreThresholdLux = mAmbientLux;
setAmbientLux(fastAmbientLux);
if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
@@ -834,11 +863,11 @@ class AutomaticBrightnessController {
// If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
// in which case we ignore the new screen brightness if it doesn't differ enough from the
// previous one.
- if (!Float.isNaN(mScreenAutoBrightness)
- && !isManuallySet
+ boolean withinThreshold = !Float.isNaN(mScreenAutoBrightness)
&& newScreenAutoBrightness > mScreenDarkeningThreshold
- && newScreenAutoBrightness < mScreenBrighteningThreshold
- && currentBrightnessWithinAllowedRange) {
+ && newScreenAutoBrightness < mScreenBrighteningThreshold;
+
+ if (withinThreshold && !isManuallySet && currentBrightnessWithinAllowedRange) {
if (mLoggingEnabled) {
Slog.d(TAG, "ignoring newScreenAutoBrightness: "
+ mScreenDarkeningThreshold + " < " + newScreenAutoBrightness
@@ -853,6 +882,9 @@ class AutomaticBrightnessController {
+ "mScreenAutoBrightness=" + mScreenAutoBrightness + ", "
+ "newScreenAutoBrightness=" + newScreenAutoBrightness);
}
+ if (!withinThreshold) {
+ mPreThresholdBrightness = mScreenAutoBrightness;
+ }
mScreenAutoBrightness = newScreenAutoBrightness;
mScreenBrighteningThreshold = clampScreenBrightness(
mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 67268e23c5b9..698f41f23e98 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -216,9 +216,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final float mScreenBrightnessDefault;
- // Previously logged screen brightness. Used for autobrightness event dumpsys.
- private float mPreviousScreenBrightness = Float.NaN;
-
// The minimum allowed brightness while in VR.
private final float mScreenBrightnessForVrRangeMinimum;
@@ -394,8 +391,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final Runnable mOnBrightnessChangeRunnable;
- // Used for keeping record in dumpsys for when and to which brightness auto adaptions were made.
- private RingBuffer<AutobrightnessEvent> mAutobrightnessEventRingBuffer;
+ private final BrightnessEvent mLastBrightnessEvent;
+ private final BrightnessEvent mTempBrightnessEvent;
+
+ // Keeps a record of brightness changes for dumpsys.
+ private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer;
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -455,6 +455,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
private float mTemporaryAutoBrightnessAdjustment;
+ private boolean mIsRbcActive;
+
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
private ObjectAnimator mColorFadeOffAnimator;
@@ -481,6 +483,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
mDisplayStatsId = mUniqueDisplayId.hashCode();
mHandler = new DisplayControllerHandler(handler.getLooper());
+ mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
+ mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
if (mDisplayId == Display.DEFAULT_DISPLAY) {
mBatteryStats = BatteryStatsService.getService();
@@ -634,8 +638,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
for (int i = 0; i < mNitsRange.length; i++) {
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
- mAutomaticBrightnessController.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
- adjustedNits);
+ mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
+ mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
// If rbc is turned on, off or there is a change in strength, we want to reset the short
@@ -991,8 +995,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong());
- mAutobrightnessEventRingBuffer =
- new RingBuffer<>(AutobrightnessEvent.class, RINGBUFFER_MAX);
+ mBrightnessEventRingBuffer =
+ new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -1088,6 +1092,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
boolean mustInitialize = false;
int brightnessAdjustmentFlags = 0;
mBrightnessReasonTemp.set(null);
+ mTempBrightnessEvent.reset();
synchronized (mLock) {
if (mStopped) {
return;
@@ -1314,7 +1319,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
- brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness();
+ brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
+ mTempBrightnessEvent);
newAutoBrightnessAdjustment =
mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
}
@@ -1376,6 +1382,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// we broadcast this change through setting.
final float unthrottledBrightnessState = brightnessState;
if (mBrightnessThrottler.isThrottled()) {
+ mTempBrightnessEvent.thermalMax = mBrightnessThrottler.getBrightnessCap();
brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
if (!mAppliedThrottling) {
@@ -1567,13 +1574,35 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
}
- // Add any automatic changes to autobrightness ringbuffer for dumpsys.
- if (mBrightnessReason.reason == BrightnessReason.REASON_AUTOMATIC
- && !BrightnessSynchronizer.floatEquals(
- mPreviousScreenBrightness, brightnessState)) {
- mPreviousScreenBrightness = brightnessState;
- mAutobrightnessEventRingBuffer.append(new AutobrightnessEvent(
- System.currentTimeMillis(), brightnessState));
+
+ // Log brightness events when a detail of significance has changed. Generally this is the
+ // brightness itself changing, but also includes data like HBM cap, thermal throttling
+ // brightness cap, RBC state, etc.
+ mTempBrightnessEvent.time = System.currentTimeMillis();
+ mTempBrightnessEvent.brightness = brightnessState;
+ mTempBrightnessEvent.reason.set(mBrightnessReason);
+ mTempBrightnessEvent.hbmMax = mHbmController.getCurrentBrightnessMax();
+ mTempBrightnessEvent.hbmMode = mHbmController.getHighBrightnessMode();
+ mTempBrightnessEvent.flags |= (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0);
+ // 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 =
+ mTempBrightnessEvent.reason.reason == BrightnessReason.REASON_TEMPORARY
+ && mLastBrightnessEvent.reason.reason == BrightnessReason.REASON_TEMPORARY;
+ if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
+ || brightnessAdjustmentFlags != 0) {
+ mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
+ BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
+
+ // Adjustment flags (and user-set flag) only get added after the equality checks since
+ // they are transient.
+ newEvent.adjustmentFlags = brightnessAdjustmentFlags;
+ newEvent.flags |= (userSetBrightnessChanged ? BrightnessEvent.FLAG_USER_SET : 0);
+ Slog.i(TAG, newEvent.toString(/* includeTime= */ false));
+
+ if (mBrightnessEventRingBuffer != null) {
+ mBrightnessEventRingBuffer.append(newEvent);
+ }
}
// Update display white-balance.
@@ -2482,6 +2511,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mPendingScreenOff=" + mPendingScreenOff);
pw.println(" mReportedToPolicy="
+ reportedToPolicyToString(mReportedScreenStateToPolicy));
+ pw.println(" mIsRbcActive=" + mIsRbcActive);
if (mScreenBrightnessRampAnimator != null) {
pw.println(" mScreenBrightnessRampAnimator.isAnimating()="
@@ -2503,7 +2533,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.dump(pw);
- dumpAutobrightnessEvents(pw);
+ dumpBrightnessEvents(pw);
}
if (mHbmController != null) {
@@ -2560,16 +2590,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
- private void dumpAutobrightnessEvents(PrintWriter pw) {
- int size = mAutobrightnessEventRingBuffer.size();
+ private void dumpBrightnessEvents(PrintWriter pw) {
+ int size = mBrightnessEventRingBuffer.size();
if (size < 1) {
pw.println("No Automatic Brightness Adjustments");
return;
}
pw.println("Automatic Brightness Adjustments Last " + size + " Events: ");
- AutobrightnessEvent[] eventArray = mAutobrightnessEventRingBuffer.toArray();
- for (int i = 0; i < mAutobrightnessEventRingBuffer.size(); i++) {
+ BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray();
+ for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) {
pw.println(" " + eventArray[i].toString());
}
}
@@ -2646,18 +2676,115 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
- private static class AutobrightnessEvent {
- final long mTime;
- final float mBrightness;
-
- AutobrightnessEvent(long time, float brightness) {
- mTime = time;
- mBrightness = brightness;
+ class BrightnessEvent {
+ static final int FLAG_RBC = 0x1;
+ static final int FLAG_INVALID_LUX = 0x2;
+ static final int FLAG_DOZE_SCALE = 0x3;
+ static final int FLAG_USER_SET = 0x4;
+
+ public final BrightnessReason reason = new BrightnessReason();
+
+ public int displayId;
+ public float lux;
+ public float preThresholdLux;
+ public long time;
+ public float brightness;
+ public float recommendedBrightness;
+ public float preThresholdBrightness;
+ public float hbmMax;
+ public float thermalMax;
+ public int hbmMode;
+ public int flags;
+ public int adjustmentFlags;
+
+ BrightnessEvent(BrightnessEvent that) {
+ copyFrom(that);
+ }
+
+ BrightnessEvent(int displayId) {
+ this.displayId = displayId;
+ reset();
+ }
+
+ void copyFrom(BrightnessEvent that) {
+ displayId = that.displayId;
+ time = that.time;
+ lux = that.lux;
+ preThresholdLux = that.preThresholdLux;
+ brightness = that.brightness;
+ recommendedBrightness = that.recommendedBrightness;
+ preThresholdBrightness = that.preThresholdBrightness;
+ hbmMax = that.hbmMax;
+ thermalMax = that.thermalMax;
+ flags = that.flags;
+ hbmMode = that.hbmMode;
+ reason.set(that.reason);
+ adjustmentFlags = that.adjustmentFlags;
+ }
+
+ void reset() {
+ time = SystemClock.uptimeMillis();
+ brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ recommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ lux = 0;
+ preThresholdLux = 0;
+ preThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ hbmMax = PowerManager.BRIGHTNESS_MAX;
+ thermalMax = PowerManager.BRIGHTNESS_MAX;
+ flags = 0;
+ hbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
+ reason.set(null);
+ adjustmentFlags = 0;
+ }
+
+ boolean equalsMainData(BrightnessEvent that) {
+ // This equals comparison purposefully ignores time since it is regularly changing and
+ // we don't want to log a brightness event just because the time changed.
+ return displayId == that.displayId
+ && Float.floatToRawIntBits(brightness)
+ == Float.floatToRawIntBits(that.brightness)
+ && Float.floatToRawIntBits(recommendedBrightness)
+ == Float.floatToRawIntBits(that.recommendedBrightness)
+ && Float.floatToRawIntBits(preThresholdBrightness)
+ == Float.floatToRawIntBits(that.preThresholdBrightness)
+ && Float.floatToRawIntBits(lux) == Float.floatToRawIntBits(that.lux)
+ && Float.floatToRawIntBits(preThresholdLux)
+ == Float.floatToRawIntBits(that.preThresholdLux)
+ && Float.floatToRawIntBits(hbmMax) == Float.floatToRawIntBits(that.hbmMax)
+ && hbmMode == that.hbmMode
+ && Float.floatToRawIntBits(thermalMax)
+ == Float.floatToRawIntBits(that.thermalMax)
+ && flags == that.flags
+ && adjustmentFlags == that.adjustmentFlags
+ && reason.equals(that.reason);
+ }
+
+ public String toString(boolean includeTime) {
+ return (includeTime ? TimeUtils.formatForLogging(time) + " - " : "")
+ + "BrightnessEvent: "
+ + "disp=" + displayId
+ + ", brt=" + brightness + ((flags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
+ + ", rcmdBrt=" + recommendedBrightness
+ + ", preBrt=" + preThresholdBrightness
+ + ", lux=" + lux
+ + ", preLux=" + preThresholdLux
+ + ", hbmMax=" + hbmMax
+ + ", hbmMode=" + BrightnessInfo.hbmToString(hbmMode)
+ + ", thrmMax=" + thermalMax
+ + ", flags=" + flagsToString()
+ + ", reason=" + reason.toString(adjustmentFlags);
}
@Override
public String toString() {
- return TimeUtils.formatForLogging(mTime) + " - Brightness: " + mBrightness;
+ return toString(/* includeTime */ true);
+ }
+
+ private String flagsToString() {
+ return ((flags & FLAG_USER_SET) != 0 ? "user_set " : "")
+ + ((flags & FLAG_RBC) != 0 ? "rbc " : "")
+ + ((flags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
+ + ((flags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "");
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 982ac3c84482..fa2f5000abbc 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,8 @@ import static android.view.Display.Mode.INVALID_MODE_ID;
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -677,11 +679,16 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
+
+ final Point stableDisplaySize = getOverlayContext()
+ .getSystemService(DisplayManager.class).getStableDisplaySize();
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
- mInfo.uniqueId, mInfo.width, mInfo.height);
+ mInfo.uniqueId, stableDisplaySize.x, stableDisplaySize.y, mInfo.width,
+ mInfo.height);
mInfo.roundedCorners = RoundedCorners.fromResources(
- res, mInfo.uniqueId, mInfo.width, mInfo.height);
+ res, mInfo.uniqueId, stableDisplaySize.x, stableDisplaySize.y, mInfo.width,
+ mInfo.height);
mInfo.installOrientation = mStaticDisplayInfo.installOrientation;
if (mStaticDisplayInfo.isInternal) {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 4a1a950c6a07..4e4f4544e068 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -140,7 +140,6 @@ final class DreamController {
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName);
- intent.putExtra(DreamService.EXTRA_IS_PREVIEW, isPreviewMode);
try {
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 0de523c5f3f6..c4ff4ac6c981 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -313,6 +313,7 @@ final class InputMethodBindingController {
mSupportsStylusHw);
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
+ mService.performOnCreateInlineSuggestionsRequestLocked();
}
// reset Handwriting event receiver.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index a2d3588f0e68..b978131e175c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -24,9 +24,9 @@ import android.os.IBinder;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.LocalServices;
@@ -164,7 +164,7 @@ public abstract class InputMethodManagerInternal {
* @param session The session passed back from the accessibility service.
*/
public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session);
+ IAccessibilityInputMethodSession session);
/**
* Unbind the accessibility service with the specified accessibilityConnectionId from current
@@ -240,7 +240,7 @@ public abstract class InputMethodManagerInternal {
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c3140551bc3..ce8b9fabd5a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -155,8 +155,10 @@ import com.android.internal.annotations.GuardedBy;
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.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -286,6 +288,30 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@NonNull
private final Set<String> mNonPreemptibleInputMethods;
+ private static final class CreateInlineSuggestionsRequest {
+ @NonNull final InlineSuggestionsRequestInfo mRequestInfo;
+ @NonNull final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull final String mPackageName;
+
+ CreateInlineSuggestionsRequest(
+ @NonNull InlineSuggestionsRequestInfo requestInfo,
+ @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull String packageName) {
+ mRequestInfo = requestInfo;
+ mCallback = callback;
+ mPackageName = packageName;
+ }
+ }
+
+ /**
+ * If a request to create inline autofill suggestions comes in while the IME is unbound
+ * due to {@link #mPreventImeStartupUnlessTextEditor}, this is where it is stored, so
+ * that it may be fulfilled once the IME rebinds.
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private CreateInlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+
@UserIdInt
private int mLastSwitchUserId;
@@ -398,7 +424,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Id of the accessibility service.
final int mId;
- public IInputMethodSession mSession;
+ public IAccessibilityInputMethodSession mSession;
@Override
public String toString() {
@@ -410,7 +436,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
AccessibilitySessionState(ClientState client, int id,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
mClient = client;
mId = id;
mSession = session;
@@ -590,6 +616,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
IInputContext mCurInputContext;
/**
+ * The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
+ */
+ @Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection;
+
+ /**
* The attributes last provided by the current client.
*/
EditorInfo mCurAttribute;
@@ -2137,16 +2168,24 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
boolean touchExplorationEnabled) {
+ clearPendingInlineSuggestionsRequestLocked();
final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
try {
- IInputMethodInvoker curMethod = getCurMethodLocked();
- if (userId == mSettings.getCurrentUserId() && curMethod != null
+ if (userId == mSettings.getCurrentUserId()
&& imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
- final IInlineSuggestionsRequestCallback callbackImpl =
- new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(),
- this);
- curMethod.onCreateInlineSuggestionsRequest(requestInfo, callbackImpl);
+ mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
+ requestInfo, callback, imi.getPackageName());
+ if (getCurMethodLocked() != null) {
+ // In the normal case when the IME is connected, we can make the request here.
+ performOnCreateInlineSuggestionsRequestLocked();
+ } else {
+ // Otherwise, the next time the IME connection is established,
+ // InputMethodBindingController.mMainConnection#onServiceConnected() will call
+ // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
+ if (DEBUG) {
+ Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
+ }
+ }
} else {
callback.onInlineSuggestionsUnsupported();
}
@@ -2155,6 +2194,34 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @GuardedBy("ImfLock.class")
+ void performOnCreateInlineSuggestionsRequestLocked() {
+ if (mPendingInlineSuggestionsRequest == null) {
+ return;
+ }
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (DEBUG) {
+ Slog.d(TAG, "Performing onCreateInlineSuggestionsRequest. mCurMethod = " + curMethod);
+ }
+ if (curMethod != null) {
+ final IInlineSuggestionsRequestCallback callback =
+ new InlineSuggestionsRequestCallbackDecorator(
+ mPendingInlineSuggestionsRequest.mCallback,
+ mPendingInlineSuggestionsRequest.mPackageName,
+ mCurTokenDisplayId, getCurTokenLocked(), this);
+ curMethod.onCreateInlineSuggestionsRequest(
+ mPendingInlineSuggestionsRequest.mRequestInfo, callback);
+ } else {
+ Slog.w(TAG, "No IME connected! Abandoning inline suggestions creation request.");
+ }
+ clearPendingInlineSuggestionsRequestLocked();
+ }
+
+ @GuardedBy("ImfLock.class")
+ private void clearPendingInlineSuggestionsRequestLocked() {
+ mPendingInlineSuggestionsRequest = null;
+ }
+
private static boolean isInlineSuggestionsEnabled(InputMethodInfo imi,
boolean touchExplorationEnabled) {
return imi.isInlineSuggestionsEnabled()
@@ -2567,7 +2634,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final InputMethodInfo curInputMethodInfo = mMethodMap.get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
- final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, accessibilityInputMethodSessions,
@@ -2606,7 +2673,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
InputBindResult attachNewAccessibilityLocked(@StartInputReason int startInputReason,
boolean initial, int id) {
if (!mBoundToAccessibility) {
- AccessibilityManagerInternal.get().bindInput(mCurClient.binding);
+ AccessibilityManagerInternal.get().bindInput();
mBoundToAccessibility = true;
}
@@ -2620,14 +2687,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (startInputReason != StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY) {
final Binder startInputToken = new Binder();
setEnabledSessionForAccessibilityLocked(mCurClient.mAccessibilitySessions);
- AccessibilityManagerInternal.get().startInput(startInputToken, mCurInputContext,
+ AccessibilityManagerInternal.get().startInput(mCurRemoteAccessibilityInputConnection,
mCurAttribute, !initial /* restarting */);
}
if (accessibilitySession != null) {
final SessionState session = mCurClient.curSession;
IInputMethodSession imeSession = session == null ? null : session.session;
- final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
@@ -2638,9 +2705,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return null;
}
- private SparseArray<IInputMethodSession> createAccessibilityInputMethodSessions(
+ private SparseArray<IAccessibilityInputMethodSession> createAccessibilityInputMethodSessions(
SparseArray<AccessibilitySessionState> accessibilitySessions) {
- final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
new SparseArray<>();
if (accessibilitySessions != null) {
for (int i = 0; i < accessibilitySessions.size(); i++) {
@@ -2662,8 +2729,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
@NonNull
private InputBindResult startInputUncheckedLocked(@NonNull ClientState cs,
- IInputContext inputContext, @NonNull EditorInfo attribute,
- @StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
+ IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
+ @StartInputReason int startInputReason,
int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
final String selectedMethodId = getSelectedMethodIdLocked();
@@ -2707,6 +2776,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
advanceSequenceNumberLocked();
mCurClient = cs;
mCurInputContext = inputContext;
+ mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
mCurVirtualDisplayToScreenMatrix =
getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
mDisplayIdToShowIme);
@@ -3709,10 +3779,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, attribute, inputContext,
- unverifiedTargetSdkVersion);
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
}
@NonNull
@@ -3720,6 +3791,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
@@ -3756,7 +3828,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
- attribute, inputContext, unverifiedTargetSdkVersion, userId);
+ attribute, inputContext, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3782,7 +3855,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputReason int startInputReason, IInputMethodClient client,
@NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
- IInputContext inputContext, int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+ IInputContext inputContext,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3875,7 +3950,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ InputMethodDebug.startInputReasonToString(startInputReason));
}
if (attribute != null) {
- return startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ return startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
}
return new InputBindResult(
@@ -3916,8 +3992,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// UI for input.
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
- res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
+ attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -3955,8 +4031,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// is more room for the target window + IME.
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3986,8 +4063,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4005,8 +4083,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
unverifiedTargetSdkVersion, startInputFlags)) {
if (!sameWindowFocused) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
+ startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4034,7 +4113,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
}
}
- res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ res = startInputUncheckedLocked(cs, inputContext,
+ remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -4790,7 +4870,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
void setEnabledSessionForAccessibilityLocked(
SparseArray<AccessibilitySessionState> accessibilitySessions) {
// mEnabledAccessibilitySessions could the same object as accessibilitySessions.
- SparseArray<IInputMethodSession> disabledSessions = new SparseArray<>();
+ SparseArray<IAccessibilityInputMethodSession> disabledSessions = new SparseArray<>();
for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) {
if (!accessibilitySessions.contains(mEnabledAccessibilitySessions.keyAt(i))) {
AccessibilitySessionState sessionState = mEnabledAccessibilitySessions.valueAt(i);
@@ -4804,7 +4884,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
AccessibilityManagerInternal.get().setImeSessionEnabled(disabledSessions,
false);
}
- SparseArray<IInputMethodSession> enabledSessions = new SparseArray<>();
+ SparseArray<IAccessibilityInputMethodSession> enabledSessions = new SparseArray<>();
for (int i = 0; i < accessibilitySessions.size(); i++) {
if (!mEnabledAccessibilitySessions.contains(accessibilitySessions.keyAt(i))) {
AccessibilitySessionState sessionState = accessibilitySessions.valueAt(i);
@@ -5649,7 +5729,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
- IInputMethodSession session) {
+ IAccessibilityInputMethodSession session) {
synchronized (ImfLock.class) {
if (mCurClient != null) {
clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index de8e06aeac9f..111621da06ce 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1058,31 +1058,35 @@ public class ContextHubService extends IContextHubService.Stub {
}
int msgVersion = 0;
- int callbacksCount = mCallbacksList.beginBroadcast();
- if (DEBUG_LOG_ENABLED) {
- Log.v(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle "
- + contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
- + callbacksCount);
- }
-
- if (callbacksCount < 1) {
+ // Synchronize access to mCallbacksList to prevent more than one outstanding broadcast as
+ // that will cause a crash.
+ synchronized (mCallbacksList) {
+ int callbacksCount = mCallbacksList.beginBroadcast();
if (DEBUG_LOG_ENABLED) {
- Log.v(TAG, "No message callbacks registered.");
+ Log.v(TAG, "Sending message " + msgType + " version " + msgVersion
+ + " from hubHandle " + contextHubHandle + ", appInstance " + appInstance
+ + ", callBackCount " + callbacksCount);
}
- return 0;
- }
- ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data);
- for (int i = 0; i < callbacksCount; ++i) {
- IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
- try {
- callback.onMessageReceipt(contextHubHandle, appInstance, msg);
- } catch (RemoteException e) {
- Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
- continue;
+ if (callbacksCount < 1) {
+ if (DEBUG_LOG_ENABLED) {
+ Log.v(TAG, "No message callbacks registered.");
+ }
+ return 0;
+ }
+
+ ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data);
+ for (int i = 0; i < callbacksCount; ++i) {
+ IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
+ try {
+ callback.onMessageReceipt(contextHubHandle, appInstance, msg);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
+ continue;
+ }
}
+ mCallbacksList.finishBroadcast();
}
- mCallbacksList.finishBroadcast();
return 0;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 2a66e026438a..320b06f6dc3e 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -629,12 +629,17 @@ public class Installer extends SystemService {
}
}
- public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath)
+ /**
+ * Dumps profiles associated with a package in a human readable format.
+ */
+ public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath,
+ boolean dumpClassesAndMethods)
throws InstallerException {
if (!checkBeforeRemote()) return false;
BlockGuard.getVmPolicy().onPathAccess(codePath);
try {
- return mInstalld.dumpProfiles(uid, packageName, profileName, codePath);
+ return mInstalld.dumpProfiles(uid, packageName, profileName, codePath,
+ dumpClassesAndMethods);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ffd924edc7d8..7fd28f678aed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4665,7 +4665,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
- public void dumpProfiles(String packageName) {
+ public void dumpProfiles(String packageName, boolean dumpClassesAndMethods) {
/* Only the shell, root, or the app user should be able to dump profiles. */
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshotComputer();
@@ -4683,7 +4683,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
- mArtManagerService.dumpProfiles(pkg);
+ mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7b0659332412..78a600e34dae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1994,8 +1994,23 @@ class PackageManagerShellCommand extends ShellCommand {
}
private int runDumpProfiles() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean dumpClassesAndMethods = false;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--dump-classes-and-methods":
+ dumpClassesAndMethods = true;
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
String packageName = getNextArg();
- mInterface.dumpProfiles(packageName);
+ mInterface.dumpProfiles(packageName, dumpClassesAndMethods);
return 0;
}
@@ -4164,9 +4179,12 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE");
pw.println(" Reconciles the package secondary dex files with the generated oat files.");
pw.println("");
- pw.println(" dump-profiles TARGET-PACKAGE");
+ pw.println(" dump-profiles [--dump-classes-and-methods] TARGET-PACKAGE");
pw.println(" Dumps method/class profile files to");
- pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + "TARGET-PACKAGE.txt");
+ pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION
+ + "TARGET-PACKAGE-primary.prof.txt.");
+ pw.println(" --dump-classes-and-methods: passed along to the profman binary to");
+ pw.println(" switch to the format used by 'profman --create-profile-from'.");
pw.println("");
pw.println(" snapshot-profile TARGET-PACKAGE [--code-path path]");
pw.println(" Take a snapshot of the package profiles to");
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 0b69cd376bd1..5fc916f888f3 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
@@ -176,6 +177,19 @@ final class ReconcilePackageUtils {
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails);
if (mergedDetails != sharedSigningDetails) {
+ // Use the restricted merge rule with the signing lineages from the
+ // other packages in the sharedUserId to ensure if any revoke a
+ // capability from a previous signer then this is reflected in the
+ // shared lineage.
+ for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) {
+ if (androidPackage.getPackageName() != null
+ && !androidPackage.getPackageName().equals(
+ parsedPackage.getPackageName())) {
+ mergedDetails = mergedDetails.mergeLineageWith(
+ androidPackage.getSigningDetails(),
+ MERGE_RESTRICTED_CAPABILITY);
+ }
+ }
sharedUserSetting.signatures.mSigningDetails =
mergedDetails;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index b3723fb61f5a..f57eaaef25a4 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -894,8 +894,12 @@ class ShortcutPackage extends ShortcutPackageItem {
// Get the list of all dynamic shortcuts in this package.
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
+ // included in the result
findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
- ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+ ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
+ mShortcutUser.mService.mContext.getPackageName(),
+ 0, /*getPinnedByAnyLauncher=*/ false);
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
for (int i = 0; i < shortcuts.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 9fb1f8f11999..60864a37ef45 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -95,7 +95,7 @@ import java.util.Set;
* </code></pre>
*/
class UserSystemPackageInstaller {
- private static final String TAG = "UserManagerService";
+ private static final String TAG = UserSystemPackageInstaller.class.getSimpleName();
private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 4fae6b8e1271..0e46b0f42a91 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -465,14 +465,15 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
/**
* Dumps the profiles for the given package.
*/
- public void dumpProfiles(AndroidPackage pkg) {
+ public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods) {
final int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
try {
ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
String codePath = packageProfileNames.keyAt(i);
String profileName = packageProfileNames.valueAt(i);
- mInstaller.dumpProfiles(sharedGid, pkg.getPackageName(), profileName, codePath);
+ mInstaller.dumpProfiles(sharedGid, pkg.getPackageName(), profileName, codePath,
+ dumpClassesAndMethods);
}
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dump profiles", e);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e58d73646bd6..0311524cd768 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -613,6 +613,10 @@ final class DefaultPermissionGrantPolicy {
pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
}
+ // SearchSelector
+ grantPermissionsToSystemPackage(pm, getDefaultSearchSelectorPackage(), userId,
+ NOTIFICATION_PERMISSIONS);
+
// Camera
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, MediaStore.ACTION_IMAGE_CAPTURE, userId),
@@ -899,15 +903,6 @@ final class DefaultPermissionGrantPolicy {
COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
- // Content capture
- String contentCapturePackageName =
- mContext.getPackageManager().getContentCaptureServicePackageName();
- if (!TextUtils.isEmpty(contentCapturePackageName)) {
- grantPermissionsToSystemPackage(pm, contentCapturePackageName, userId,
- PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
- }
-
// Attention Service
String attentionServicePackageName =
mContext.getPackageManager().getAttentionServicePackageName();
@@ -941,6 +936,10 @@ final class DefaultPermissionGrantPolicy {
new Intent(Intent.ACTION_MAIN).addCategory(category), userId);
}
+ private String getDefaultSearchSelectorPackage() {
+ return mContext.getString(R.string.config_defaultSearchSelectorPackageName);
+ }
+
@SafeVarargs
private final void grantPermissionToEachSystemPackage(PackageManagerWrapper pm,
ArrayList<String> packages, int userId, Set<String>... permissions) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 186eccc80f8a..74f6296ef4eb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -195,6 +195,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
/** All storage permissions */
private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>();
+
+ private static final Set<String> READ_MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
+
+ private static final Set<String> READ_MEDIA_VISUAL_PERMISSIONS = new ArraySet<>();
+
/** All nearby devices permissions */
private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>();
@@ -222,10 +227,10 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
+ READ_MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
+ READ_MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
+ READ_MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
+ READ_MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
@@ -2070,7 +2075,10 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
PermissionInfo permInfo = getPermissionInfo(
newPackage.getRequestedPermissions().get(i),
newPackage.getPackageName(), 0);
- if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+ boolean isStorageOrMedia = STORAGE_PERMISSIONS.contains(permInfo.name)
+ || READ_MEDIA_AURAL_PERMISSIONS.contains(permInfo.name)
+ || READ_MEDIA_VISUAL_PERMISSIONS.contains(permInfo.name);
+ if (permInfo == null || !isStorageOrMedia) {
continue;
}
@@ -2645,12 +2653,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
// Cache newImplicitPermissions before modifing permissionsState as for the
// shared uids the original and new state are the same object
- // TODO(205888750): remove the line for LEGACY_REVIEW once propagated through
- // droidfood
if (!origState.hasPermissionState(permName)
&& (pkg.getImplicitPermissions().contains(permName)
- || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))
- || NOTIFICATION_PERMISSIONS.contains(permName)) {
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.getImplicitPermissions().contains(permName)) {
// If permName is an implicit permission, try to auto-grant
newImplicitPermissions.add(permName);
@@ -2808,12 +2813,14 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
// Remove review flag as it is not necessary anymore
- if (!NOTIFICATION_PERMISSIONS.contains(perm)) {
+ // TODO(b/227186603) re-enable check for notification permission once
+ // droidfood state has been cleared
+ //if (!NOTIFICATION_PERMISSIONS.contains(perm)) {
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
- }
+ //}
if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0
&& !isPermissionSplitFromNonRuntime(permName,
@@ -3145,7 +3152,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
if (bp.isRuntime()) {
- if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ if (!(newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)
+ || READ_MEDIA_AURAL_PERMISSIONS.contains(newPerm)
+ || READ_MEDIA_VISUAL_PERMISSIONS.contains(newPerm))) {
ps.updatePermissionFlags(bp,
FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 32e7a6a81096..89ac9e773906 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -57,6 +57,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
+import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -78,10 +79,12 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.policy.AttributeCache;
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
@@ -1064,7 +1067,8 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
- activityInfo.packageName, info.intent, info.checkedOptions, true)) {
+ activityInfo.packageName, info.intent, info.checkedOptions, true)
+ || isNoDisplayActivity(activityInfo)) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1139,6 +1143,22 @@ public final class PermissionPolicyService extends SystemService {
taskInfo, currPkg, intent, null, false);
}
+ private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) {
+ final int themeResource = aInfo.getThemeResource();
+ if (themeResource == Resources.ID_NULL) {
+ return false;
+ }
+
+ boolean noDisplay = false;
+ final AttributeCache.Entry ent = AttributeCache.instance()
+ .get(aInfo.packageName, themeResource, R.styleable.Window, 0);
+ if (ent != null) {
+ noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ }
+
+ return noDisplay;
+ }
+
/**
* Determine if a particular task is in the proper state to show a system-triggered
* permission prompt. A prompt can be shown if the task is just starting, or the task is
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e0da0e8bdf35..4075cddc302c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -293,6 +293,7 @@ public final class PowerManagerService extends SystemService
private final Clock mClock;
private final Injector mInjector;
+ private AppOpsManager mAppOpsManager;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
private DisplayManagerInternal mDisplayManagerInternal;
@@ -990,6 +991,10 @@ public final class PowerManagerService extends SystemService
LowPowerStandbyController createLowPowerStandbyController(Context context, Looper looper) {
return new LowPowerStandbyController(context, looper, SystemClock::elapsedRealtime);
}
+
+ AppOpsManager createAppOpsManager(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
final Constants mConstants;
@@ -1044,6 +1049,8 @@ public final class PowerManagerService extends SystemService
mInattentiveSleepWarningOverlayController =
mInjector.createInattentiveSleepWarningController();
+ mAppOpsManager = injector.createAppOpsManager(mContext);
+
mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener();
// Save brightness values:
@@ -1562,8 +1569,7 @@ public final class PowerManagerService extends SystemService
}
return true;
}
- if (mContext.getSystemService(AppOpsManager.class).checkOpNoThrow(
- AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName)
+ if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName)
== AppOpsManager.MODE_ALLOWED) {
if (DEBUG_SPEW) {
Slog.d(TAG, "Allowing device wake-up for app with special access " + opPackageName);
diff --git a/services/core/java/com/android/server/trust/TEST_MAPPING b/services/core/java/com/android/server/trust/TEST_MAPPING
index be8ed67f459b..fa46acd9c39b 100644
--- a/services/core/java/com/android/server/trust/TEST_MAPPING
+++ b/services/core/java/com/android/server/trust/TEST_MAPPING
@@ -11,5 +11,18 @@
}
]
}
+ ],
+ "trust-tablet": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 89470ec00a6c..5c305c6902af 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -29,6 +29,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -47,6 +48,8 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import java.util.ArrayList;
import java.util.Collections;
@@ -95,6 +98,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
// TODO (Android T+): Add ability to handle multiple subIds per slot.
@NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
+
+ @NonNull
+ private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>();
+
@NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
@NonNull
@@ -250,7 +257,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
final TelephonySubscriptionSnapshot newSnapshot =
new TelephonySubscriptionSnapshot(
- mDeps.getActiveDataSubscriptionId(), newSubIdToInfoMap, privilegedPackages);
+ mDeps.getActiveDataSubscriptionId(),
+ newSubIdToInfoMap,
+ mSubIdToCarrierConfigMap,
+ privilegedPackages);
// If snapshot was meaningfully updated, fire the callback
if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -311,47 +321,77 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
}
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
- if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) {
+ final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
+ if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
+
+ final PersistableBundle minimized =
+ PersistableBundleUtils.minimizeBundle(
+ carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ if (minimized != null) {
+ mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized));
+ }
handleSubscriptionsChanged();
}
} else {
- mReadySubIdsBySlotId.remove(slotId);
+ final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId);
+ if (oldSubid != null) {
+ mSubIdToCarrierConfigMap.remove(oldSubid);
+ }
handleSubscriptionsChanged();
}
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) {
+ mReadySubIdsBySlotId.clear();
mReadySubIdsBySlotId.putAll(readySubIdsBySlotId);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setSubIdToCarrierConfigMap(
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) {
+ mSubIdToCarrierConfigMap.clear();
+ mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
Map<Integer, Integer> getReadySubIdsBySlotId() {
return Collections.unmodifiableMap(mReadySubIdsBySlotId);
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() {
+ return Collections.unmodifiableMap(mSubIdToCarrierConfigMap);
+ }
+
/** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
public static class TelephonySubscriptionSnapshot {
private final int mActiveDataSubId;
private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
+ private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap;
private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
new TelephonySubscriptionSnapshot(
- INVALID_SUBSCRIPTION_ID, Collections.emptyMap(), Collections.emptyMap());
+ INVALID_SUBSCRIPTION_ID,
+ Collections.emptyMap(),
+ Collections.emptyMap(),
+ Collections.emptyMap());
@VisibleForTesting(visibility = Visibility.PRIVATE)
TelephonySubscriptionSnapshot(
int activeDataSubId,
@NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
+ @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
@NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
mActiveDataSubId = activeDataSubId;
Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
+ Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null");
mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
+ mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap);
final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
@@ -423,9 +463,40 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
: false;
}
+ /**
+ * Retrieves a carrier config for a subscription in the provided group.
+ *
+ * <p>This method will prioritize non-opportunistic subscriptions, but will use the a
+ * carrier config for an opportunistic subscription if no other subscriptions are found.
+ */
+ @Nullable
+ public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) {
+ PersistableBundleWrapper result = null;
+
+ for (int subId : getAllSubIdsInGroup(subGrp)) {
+ final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId);
+ if (config != null) {
+ result = config;
+
+ // Attempt to use (any) non-opportunistic subscription. If this subscription is
+ // opportunistic, continue and try to find a non-opportunistic subscription,
+ // using the opportunistic ones as a last resort.
+ if (!isOpportunistic(subId)) {
+ return config;
+ }
+ }
+ }
+
+ return result;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mActiveDataSubId, mSubIdToInfoMap, mPrivilegedPackages);
+ return Objects.hash(
+ mActiveDataSubId,
+ mSubIdToInfoMap,
+ mSubIdToCarrierConfigMap,
+ mPrivilegedPackages);
}
@Override
@@ -438,6 +509,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
return mActiveDataSubId == other.mActiveDataSubId
&& mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
+ && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap)
&& mPrivilegedPackages.equals(other.mPrivilegedPackages);
}
@@ -448,6 +520,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
pw.println("mActiveDataSubId: " + mActiveDataSubId);
pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
+ pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap);
pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
pw.decreaseIndent();
@@ -458,6 +531,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
return "TelephonySubscriptionSnapshot{ "
+ "mActiveDataSubId=" + mActiveDataSubId
+ ", mSubIdToInfoMap=" + mSubIdToInfoMap
+ + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap
+ ", mPrivilegedPackages=" + mPrivilegedPackages
+ " }";
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index cefd8efe9658..05df22f124ed 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,6 +61,7 @@ import android.net.ipsec.ike.ChildSessionParams;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeException;
@@ -509,6 +510,42 @@ public class VcnGatewayConnection extends StateMachine {
}
}
+ /**
+ * Sent when an IKE session connection information has changed.
+ *
+ * <p>This signal is always fired before EVENT_SETUP_COMPLETED and EVENT_MIGRATION_COMPLETED.
+ *
+ * <p>Only relevant in the Connecting and Connected state.
+ *
+ * @param arg1 The session token for the IKE Session whose connection information has changed,
+ * used to prevent out-of-date signals from propagating.
+ * @param obj @NonNull An EventIkeConnectionInfoChangedInfo instance with relevant data.
+ */
+ private static final int EVENT_IKE_CONNECTION_INFO_CHANGED = 12;
+
+ private static class EventIkeConnectionInfoChangedInfo implements EventInfo {
+ @NonNull public final IkeSessionConnectionInfo ikeConnectionInfo;
+
+ EventIkeConnectionInfoChangedInfo(@NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+ this.ikeConnectionInfo = ikeConnectionInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ikeConnectionInfo);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof EventIkeConnectionInfoChangedInfo)) {
+ return false;
+ }
+
+ final EventIkeConnectionInfoChangedInfo rhs = (EventIkeConnectionInfoChangedInfo) other;
+ return Objects.equals(ikeConnectionInfo, rhs.ikeConnectionInfo);
+ }
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -624,6 +661,14 @@ public class VcnGatewayConnection extends StateMachine {
private UnderlyingNetworkRecord mUnderlying;
/**
+ * The current IKE Session connection information
+ *
+ * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
+ * states, @Nullable otherwise.
+ */
+ private IkeSessionConnectionInfo mIkeConnectionInfo;
+
+ /**
* The active IKE session.
*
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
@@ -1197,6 +1242,14 @@ public class VcnGatewayConnection extends StateMachine {
exceptionMessage);
}
+ private void ikeConnectionInfoChanged(
+ int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_IKE_CONNECTION_INFO_CHANGED,
+ token,
+ new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo));
+ }
+
private void sessionClosed(int token, @Nullable Exception exception) {
if (exception != null) {
notifyStatusCallbackForSessionClosed(exception);
@@ -1313,7 +1366,8 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
- case EVENT_MIGRATION_COMPLETED:
+ case EVENT_MIGRATION_COMPLETED: // Fallthrough
+ case EVENT_IKE_CONNECTION_INFO_CHANGED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -1592,6 +1646,7 @@ public class VcnGatewayConnection extends StateMachine {
transitionTo(mDisconnectingState);
break;
case EVENT_SETUP_COMPLETED: // fallthrough
+ case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough
case EVENT_TRANSFORM_CREATED:
// Child setup complete; move to ConnectedState for NetworkAgent registration
deferMessage(msg);
@@ -1614,12 +1669,17 @@ public class VcnGatewayConnection extends StateMachine {
protected void updateNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnNetworkAgent agent,
- @NonNull VcnChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
- mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+ mConnectionConfig,
+ tunnelIface,
+ childConfig,
+ mUnderlying,
+ ikeConnectionInfo);
agent.sendNetworkCapabilities(caps);
agent.sendLinkProperties(lp);
@@ -1630,12 +1690,17 @@ public class VcnGatewayConnection extends StateMachine {
protected VcnNetworkAgent buildNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull VcnChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
- mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+ mConnectionConfig,
+ tunnelIface,
+ childConfig,
+ mUnderlying,
+ ikeConnectionInfo);
final NetworkAgentConfig nac =
new NetworkAgentConfig.Builder()
.setLegacyType(ConnectivityManager.TYPE_MOBILE)
@@ -1838,7 +1903,11 @@ public class VcnGatewayConnection extends StateMachine {
mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
setupInterfaceAndNetworkAgent(
- mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig);
+ mCurrentToken,
+ mTunnelIface,
+ mChildConfig,
+ oldChildConfig,
+ mIkeConnectionInfo);
break;
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
@@ -1852,6 +1921,10 @@ public class VcnGatewayConnection extends StateMachine {
handleMigrationCompleted(migrationCompletedInfo);
break;
+ case EVENT_IKE_CONNECTION_INFO_CHANGED:
+ mIkeConnectionInfo =
+ ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1875,7 +1948,7 @@ public class VcnGatewayConnection extends StateMachine {
migrationCompletedInfo.outTransform,
IpSecManager.DIRECTION_OUT);
- updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
// Trigger re-validation after migration events.
mConnectivityManager.reportNetworkConnectivity(
@@ -1906,7 +1979,8 @@ public class VcnGatewayConnection extends StateMachine {
// Network not yet set up, or child not yet connected.
if (mNetworkAgent != null && mChildConfig != null) {
// If only network properties changed and agent is active, update properties
- updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ updateNetworkAgent(
+ mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
}
}
}
@@ -1915,13 +1989,14 @@ public class VcnGatewayConnection extends StateMachine {
int token,
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
- @NonNull VcnChildSessionConfiguration oldChildConfig) {
+ @NonNull VcnChildSessionConfiguration oldChildConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
setupInterface(token, tunnelIface, childConfig, oldChildConfig);
if (mNetworkAgent == null) {
- mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+ mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo);
} else {
- updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+ updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo);
// mNetworkAgent not null, so the VCN Network has already been established. Clear
// the failed attempt counter and safe mode alarm since this transition is complete.
@@ -2098,7 +2173,8 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
- @Nullable UnderlyingNetworkRecord underlying) {
+ @Nullable UnderlyingNetworkRecord underlying,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final IkeTunnelConnectionParams ikeTunnelParams =
gatewayConnectionConfig.getTunnelConnectionParams();
final LinkProperties lp = new LinkProperties();
@@ -2139,7 +2215,8 @@ public class VcnGatewayConnection extends StateMachine {
MtuUtils.getMtu(
ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(),
gatewayConnectionConfig.getMaxMtu(),
- underlyingMtu));
+ underlyingMtu,
+ ikeConnectionInfo.getLocalAddress() instanceof Inet4Address));
return lp;
}
@@ -2154,7 +2231,7 @@ public class VcnGatewayConnection extends StateMachine {
@Override
public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
logDbg("IkeOpened for token " + mToken);
- // Nothing to do here.
+ ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo());
}
@Override
@@ -2174,6 +2251,13 @@ public class VcnGatewayConnection extends StateMachine {
logInfo("IkeError for token " + mToken, exception);
// Non-fatal, log and continue.
}
+
+ @Override
+ public void onIkeSessionConnectionInfoChanged(
+ @NonNull IkeSessionConnectionInfo connectionInfo) {
+ logDbg("onIkeSessionConnectionInfoChanged for token " + mToken);
+ ikeConnectionInfoChanged(mToken, connectionInfo);
+ }
}
/** Implementation of ChildSessionCallback, exposed for testing. */
@@ -2350,6 +2434,11 @@ public class VcnGatewayConnection extends StateMachine {
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ IkeSessionConnectionInfo getIkeConnectionInfo() {
+ return mIkeConnectionInfo;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
boolean isQuitting() {
return mIsQuitting.getValue();
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index c96c1ee01a6d..2f84fddc7278 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -24,6 +24,7 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +35,6 @@ import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Slog;
@@ -81,7 +81,7 @@ class NetworkPriorityClassifier {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
// mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
if (networkRecord.isBlocked) {
@@ -119,7 +119,7 @@ class NetworkPriorityClassifier {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
final boolean isSelectedUnderlyingNetwork =
currentlySelected != null
@@ -181,7 +181,7 @@ class NetworkPriorityClassifier {
VcnWifiUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
if (!caps.hasTransport(TRANSPORT_WIFI)) {
@@ -204,7 +204,7 @@ class NetworkPriorityClassifier {
private static boolean isWifiRssiAcceptable(
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
final boolean isSelectedNetwork =
currentlySelected != null
@@ -314,7 +314,7 @@ class NetworkPriorityClassifier {
return false;
}
- static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ static int getWifiEntryRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
if (carrierConfig != null) {
return carrierConfig.getInt(
VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
@@ -323,7 +323,7 @@ class NetworkPriorityClassifier {
return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
}
- static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ static int getWifiExitRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
if (carrierConfig != null) {
return carrierConfig.getInt(
VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index a3babf7c9fff..d474c5d33a93 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -21,7 +21,7 @@ import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListen
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,8 +37,6 @@ import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -51,7 +49,6 @@ import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -87,7 +84,7 @@ public class UnderlyingNetworkController {
@Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- @Nullable private PersistableBundle mCarrierConfig;
+ @Nullable private PersistableBundleWrapper mCarrierConfig;
private boolean mIsQuitting = false;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@@ -124,25 +121,7 @@ public class UnderlyingNetworkController {
.getSystemService(TelephonyManager.class)
.registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
- // TODO: Listen for changes in carrier config that affect this.
- for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- PersistableBundle config =
- mVcnContext
- .getContext()
- .getSystemService(CarrierConfigManager.class)
- .getConfigForSubId(subId);
-
- if (config != null) {
- mCarrierConfig = config;
-
- // Attempt to use (any) non-opportunistic subscription. If this subscription is
- // opportunistic, continue and try to find a non-opportunistic subscription, using
- // the opportunistic ones as a last resort.
- if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) {
- break;
- }
- }
- }
+ mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
registerOrUpdateNetworkRequests();
}
@@ -334,6 +313,9 @@ public class UnderlyingNetworkController {
final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
mLastSnapshot = newSnapshot;
+ // Update carrier config
+ mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
+
// Only trigger re-registration if subIds in this group have changed
if (oldSnapshot
.getAllSubIdsInGroup(mSubscriptionGroup)
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 06f92805ad2b..319680e0b01c 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -16,6 +16,8 @@
package com.android.server.vcn.routeselection;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkProperties;
@@ -23,7 +25,6 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -68,7 +69,7 @@ public class UnderlyingNetworkRecord {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
// Never changes after the underlying network record is created.
if (mPriorityClass == PRIORITY_CLASS_INVALID) {
mPriorityClass =
@@ -113,7 +114,7 @@ public class UnderlyingNetworkRecord {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
return (left, right) -> {
final int leftIndex =
left.getOrCalculatePriorityClass(
@@ -167,7 +168,7 @@ public class UnderlyingNetworkRecord {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
index 5d40cca3e19d..356c71f5f26a 100644
--- a/services/core/java/com/android/server/vcn/util/MtuUtils.java
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -51,9 +51,20 @@ public class MtuUtils {
/**
* Max ESP overhead possible
*
- * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+ * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next
+ * Header). Note: Payload data, Pad Length and Next Header will need to be padded to be multiple
+ * of the block size of a cipher, and at the same time be aligned on a 4-byte boundary.
*/
- private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+ private static final int GENERIC_ESP_OVERHEAD_MAX_V4 = 78;
+
+ /**
+ * Max ESP overhead possible
+ *
+ * <p>40 (Outer IPv6) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next Header). Note: Payload data,
+ * Pad Length and Next Header will need to be padded to be multiple of the block size of a
+ * cipher, and at the same time be aligned on a 4-byte boundary.
+ */
+ private static final int GENERIC_ESP_OVERHEAD_MAX_V6 = 50;
/** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
@@ -108,7 +119,10 @@ public class MtuUtils {
* </ul>
*/
public static int getMtu(
- @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+ @NonNull List<ChildSaProposal> childProposals,
+ int maxMtu,
+ int underlyingMtu,
+ boolean isIpv4) {
if (underlyingMtu <= 0) {
return IPV6_MIN_MTU;
}
@@ -145,10 +159,13 @@ public class MtuUtils {
}
}
+ final int genericEspOverheadMax =
+ isIpv4 ? GENERIC_ESP_OVERHEAD_MAX_V4 : GENERIC_ESP_OVERHEAD_MAX_V6;
+
// Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
- final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - genericEspOverheadMax;
final int normalModeMtu =
- underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ underlyingMtu - maxCryptOverhead - maxAuthOverhead - genericEspOverheadMax;
return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
}
}
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index 1c675c228554..08e8eebb8740 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -28,11 +28,13 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.TreeSet;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -354,4 +356,182 @@ public class PersistableBundleUtils {
}
}
}
+
+ /**
+ * Returns a copy of the persistable bundle with only the specified keys
+ *
+ * <p>This allows for holding minimized copies for memory-saving purposes.
+ */
+ @NonNull
+ public static PersistableBundle minimizeBundle(
+ @NonNull PersistableBundle bundle, String... keys) {
+ final PersistableBundle minimized = new PersistableBundle();
+
+ if (bundle == null) {
+ return minimized;
+ }
+
+ for (String key : keys) {
+ if (bundle.containsKey(key)) {
+ final Object value = bundle.get(key);
+ if (value == null) {
+ continue;
+ }
+
+ if (value instanceof Boolean) {
+ minimized.putBoolean(key, (Boolean) value);
+ } else if (value instanceof boolean[]) {
+ minimized.putBooleanArray(key, (boolean[]) value);
+ } else if (value instanceof Double) {
+ minimized.putDouble(key, (Double) value);
+ } else if (value instanceof double[]) {
+ minimized.putDoubleArray(key, (double[]) value);
+ } else if (value instanceof Integer) {
+ minimized.putInt(key, (Integer) value);
+ } else if (value instanceof int[]) {
+ minimized.putIntArray(key, (int[]) value);
+ } else if (value instanceof Long) {
+ minimized.putLong(key, (Long) value);
+ } else if (value instanceof long[]) {
+ minimized.putLongArray(key, (long[]) value);
+ } else if (value instanceof String) {
+ minimized.putString(key, (String) value);
+ } else if (value instanceof String[]) {
+ minimized.putStringArray(key, (String[]) value);
+ } else if (value instanceof PersistableBundle) {
+ minimized.putPersistableBundle(key, (PersistableBundle) value);
+ } else {
+ continue;
+ }
+ }
+ }
+
+ return minimized;
+ }
+
+ /** Builds a stable hashcode */
+ public static int getHashCode(@Nullable PersistableBundle bundle) {
+ if (bundle == null) {
+ return -1;
+ }
+
+ int iterativeHashcode = 0;
+ TreeSet<String> treeSet = new TreeSet<>(bundle.keySet());
+ for (String key : treeSet) {
+ Object val = bundle.get(key);
+ if (val instanceof PersistableBundle) {
+ iterativeHashcode =
+ Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
+ } else {
+ iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
+ }
+ }
+
+ return iterativeHashcode;
+ }
+
+ /** Checks for persistable bundle equality */
+ public static boolean isEqual(
+ @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
+ // Check for pointer equality & null equality
+ if (Objects.equals(left, right)) {
+ return true;
+ }
+
+ // If only one of the two is null, but not the other, not equal by definition.
+ if (Objects.isNull(left) != Objects.isNull(right)) {
+ return false;
+ }
+
+ if (!left.keySet().equals(right.keySet())) {
+ return false;
+ }
+
+ for (String key : left.keySet()) {
+ Object leftVal = left.get(key);
+ Object rightVal = right.get(key);
+
+ // Check for equality
+ if (Objects.equals(leftVal, rightVal)) {
+ continue;
+ } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) {
+ // If only one of the two is null, but not the other, not equal by definition.
+ return false;
+ } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
+ // If classes are different, not equal by definition.
+ return false;
+ }
+ if (leftVal instanceof PersistableBundle) {
+ if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
+ return false;
+ }
+ } else if (leftVal.getClass().isArray()) {
+ if (leftVal instanceof boolean[]) {
+ if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof double[]) {
+ if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof int[]) {
+ if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof long[]) {
+ if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) {
+ return false;
+ }
+ } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) {
+ return false;
+ }
+ } else {
+ if (!Objects.equals(leftVal, rightVal)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Wrapper class around PersistableBundles to allow equality comparisons
+ *
+ * <p>This class exposes the minimal getters to retrieve values.
+ */
+ public static class PersistableBundleWrapper {
+ @NonNull private final PersistableBundle mBundle;
+
+ public PersistableBundleWrapper(@NonNull PersistableBundle bundle) {
+ mBundle = Objects.requireNonNull(bundle, "Bundle was null");
+ }
+
+ /**
+ * Retrieves the integer associated with the provided key.
+ *
+ * @param key the string key to query
+ * @param defaultValue the value to return if key does not exist
+ * @return the int value, or the default
+ */
+ public int getInt(String key, int defaultValue) {
+ return mBundle.getInt(key, defaultValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return getHashCode(mBundle);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PersistableBundleWrapper)) {
+ return false;
+ }
+
+ final PersistableBundleWrapper other = (PersistableBundleWrapper) obj;
+
+ return isEqual(mBundle, other.mBundle);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 5fdcd690b5e2..4822ddbc0ebb 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1190,10 +1190,29 @@ class ActivityClientController extends IActivityClientController.Stub {
}
}
+ /**
+ * Removes the outdated home task snapshot.
+ *
+ * @param token The token of the home task, or null if you have the
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}
+ * permission and want us to find the home task token for you.
+ */
@Override
public void invalidateHomeTaskSnapshot(IBinder token) {
+ if (token == null) {
+ ActivityTaskManagerService.enforceTaskPermission("invalidateHomeTaskSnapshot");
+ }
+
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ final ActivityRecord r;
+ if (token == null) {
+ final Task rootTask =
+ mService.mRootWindowContainer.getDefaultTaskDisplayArea().getRootHomeTask();
+ r = rootTask != null ? rootTask.topRunningActivity() : null;
+ } else {
+ r = ActivityRecord.isInRootTaskLocked(token);
+ }
+
if (r != null && r.isActivityTypeHome()) {
mService.mWindowManager.mTaskSnapshotController.removeSnapshotCache(
r.getTask().mTaskId);
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index dbc08cd5d1a9..e7c03aa7a099 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -637,12 +637,19 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
+ registerConfigurationChangeListener(listener, true /* shouldDispatchConfig */);
+ }
+
+ void registerConfigurationChangeListener(ConfigurationContainerListener listener,
+ boolean shouldDispatchConfig) {
if (mChangeListeners.contains(listener)) {
return;
}
mChangeListeners.add(listener);
- listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
- listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
+ if (shouldDispatchConfig) {
+ listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
+ listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
+ }
}
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 466075702409..01d1614610a2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -358,6 +358,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
float mInitialPhysicalXDpi = 0.0f;
float mInitialPhysicalYDpi = 0.0f;
+ private Point mStableDisplaySize;
+
DisplayCutout mInitialDisplayCutout;
private final RotationCache<DisplayCutout, WmDisplayCutout> mDisplayCutoutCache
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
@@ -379,6 +381,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
int mBaseDisplayWidth = 0;
int mBaseDisplayHeight = 0;
+ DisplayCutout mBaseDisplayCutout;
+ RoundedCorners mBaseRoundedCorners;
boolean mIsSizeForced = false;
/**
@@ -2078,7 +2082,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
WmDisplayCutout calculateDisplayCutoutForRotation(int rotation) {
- return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
+ return mDisplayCutoutCache.getOrCompute(
+ mIsSizeForced ? mBaseDisplayCutout : mInitialDisplayCutout, rotation);
}
static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
@@ -2101,11 +2106,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
DisplayCutout cutout, int rotation) {
return calculateDisplayCutoutForRotationAndDisplaySizeUncached(cutout, rotation,
- mInitialDisplayWidth, mInitialDisplayHeight);
+ mIsSizeForced ? mBaseDisplayWidth : mInitialDisplayWidth,
+ mIsSizeForced ? mBaseDisplayHeight : mInitialDisplayHeight);
}
RoundedCorners calculateRoundedCornersForRotation(int rotation) {
- return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
+ return mRoundedCornerCache.getOrCompute(
+ mIsSizeForced ? mBaseRoundedCorners : mInitialRoundedCorners, rotation);
}
private RoundedCorners calculateRoundedCornersForRotationUncached(
@@ -2118,7 +2125,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return roundedCorners;
}
- return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ return roundedCorners.rotate(
+ rotation,
+ mIsSizeForced ? mBaseDisplayWidth : mInitialDisplayWidth,
+ mIsSizeForced ? mBaseDisplayHeight : mInitialDisplayHeight);
}
PrivacyIndicatorBounds calculatePrivacyIndicatorBoundsForRotation(int rotation) {
@@ -2719,6 +2729,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mInitialRoundedCorners = mDisplayInfo.roundedCorners;
mCurrentPrivacyIndicatorBounds = new PrivacyIndicatorBounds(new Rect[4],
mDisplayInfo.rotation);
+ mStableDisplaySize = mWmService.mDisplayManager.getStableDisplaySize();
}
/**
@@ -2814,6 +2825,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mBaseDisplayDensity = baseDensity;
mBaseDisplayPhysicalXDpi = baseXDpi;
mBaseDisplayPhysicalYDpi = baseYDpi;
+ if (mIsSizeForced) {
+ mBaseDisplayCutout = loadDisplayCutout(baseWidth, baseHeight);
+ mBaseRoundedCorners = loadRoundedCorners(baseWidth, baseHeight);
+ }
if (mMaxUiWidth > 0 && mBaseDisplayWidth > mMaxUiWidth) {
final float ratio = mMaxUiWidth / (float) mBaseDisplayWidth;
@@ -2904,6 +2919,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
}
+ DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
+ if (mDisplayPolicy == null) {
+ return null;
+ }
+ return DisplayCutout.fromResourcesRectApproximation(
+ mDisplayPolicy.getSystemUiContext().getResources(), mDisplayInfo.uniqueId,
+ mStableDisplaySize.x, mStableDisplaySize.y, displayWidth, displayHeight);
+ }
+
+ RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
+ if (mDisplayPolicy == null) {
+ return null;
+ }
+ return RoundedCorners.fromResources(
+ mDisplayPolicy.getSystemUiContext().getResources(), mDisplayInfo.uniqueId,
+ mStableDisplaySize.x, mStableDisplaySize.y, displayWidth, displayHeight);
+ }
+
@Override
void getStableRect(Rect out) {
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9ea566ece6ec..dc15cb781bc8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1086,6 +1086,12 @@ class Task extends TaskFragment {
updateTaskDescription();
}
mSupportsPictureInPicture = info.supportsPictureInPicture();
+
+ // Re-adding the task to Recents once updated
+ if (inRecents) {
+ mTaskSupervisor.mRecentTasks.remove(this);
+ mTaskSupervisor.mRecentTasks.add(this);
+ }
}
/** Sets the original minimal width and height. */
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f5af2b4e29fa..c7bc51356c76 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3747,13 +3747,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
void registerWindowContainerListener(WindowContainerListener listener) {
+ registerWindowContainerListener(listener, true /* shouldPropConfig */);
+ }
+
+ void registerWindowContainerListener(WindowContainerListener listener,
+ boolean shouldDispatchConfig) {
if (mListeners.contains(listener)) {
return;
}
mListeners.add(listener);
// Also register to ConfigurationChangeListener to receive configuration changes.
- registerConfigurationChangeListener(listener);
- listener.onDisplayChanged(getDisplayContent());
+ registerConfigurationChangeListener(listener, shouldDispatchConfig);
+ if (shouldDispatchConfig) {
+ listener.onDisplayChanged(getDisplayContent());
+ }
}
void unregisterWindowContainerListener(WindowContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 7956a112539e..912fdb2e32af 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -69,6 +69,16 @@ class WindowContextListenerController {
final ArrayMap<IBinder, WindowContextListenerImpl> mListeners = new ArrayMap<>();
/**
+ * @see #registerWindowContainerListener(IBinder, WindowContainer, int, int, Bundle, boolean)
+ */
+ void registerWindowContainerListener(@NonNull IBinder clientToken,
+ @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
+ @Nullable Bundle options) {
+ registerWindowContainerListener(clientToken, container, ownerUid, type, options,
+ true /* shouDispatchConfigWhenRegistering */);
+ }
+
+ /**
* Registers the listener to a {@code container} which is associated with
* a {@code clientToken}, which is a {@link android.window.WindowContext} representation. If the
* listener associated with {@code clientToken} hasn't been initialized yet, create one
@@ -80,15 +90,18 @@ class WindowContextListenerController {
* @param ownerUid the caller UID
* @param type the window type
* @param options a bundle used to pass window-related options.
+ * @param shouDispatchConfigWhenRegistering {@code true} to indicate the current
+ * {@code container}'s config will dispatch to the client side when
+ * registering the {@link WindowContextListenerImpl}
*/
void registerWindowContainerListener(@NonNull IBinder clientToken,
@NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
- @Nullable Bundle options) {
+ @Nullable Bundle options, boolean shouDispatchConfigWhenRegistering) {
WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) {
listener = new WindowContextListenerImpl(clientToken, container, ownerUid, type,
options);
- listener.register();
+ listener.register(shouDispatchConfigWhenRegistering);
} else {
listener.updateContainer(container);
}
@@ -228,12 +241,16 @@ class WindowContextListenerController {
}
private void register() {
+ register(true /* shouldDispatchConfig */);
+ }
+
+ private void register(boolean shouldDispatchConfig) {
final IBinder token = mClientToken.asBinder();
if (mDeathRecipient == null) {
throw new IllegalStateException("Invalid client token: " + token);
}
mListeners.putIfAbsent(token, this);
- mContainer.registerWindowContainerListener(this);
+ mContainer.registerWindowContainerListener(this, shouldDispatchConfig);
}
private void unregister() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2f4dc515fd11..c37c9cb72ec5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2770,12 +2770,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
// TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
// the feature b/155340867 is completed.
- final DisplayArea da = dc.findAreaForWindowType(type, options,
+ final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */);
- // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
- // in return value of attachWindowContextToDisplayArea.
mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
- callingUid, type, options);
+ callingUid, type, options, false /* shouDispatchConfigWhenRegistering */);
return da.getConfiguration();
}
} finally {
@@ -2871,7 +2869,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
mWindowContextListenerController.registerWindowContainerListener(clientToken, dc,
- callingUid, INVALID_WINDOW_TYPE, null /* options */);
+ callingUid, INVALID_WINDOW_TYPE, null /* options */,
+ false /* shouDispatchConfigWhenRegistering */);
return dc.getConfiguration();
}
} finally {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
index 6aef90c79705..cc32c4d8e61c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -141,11 +141,11 @@ class DeviceManagementResourcesProvider {
String drawableId, String drawableSource, String drawableStyle,
ParcelableResource updatableResource) {
synchronized (mLock) {
- Map<String, Map<String, ParcelableResource>> drawablesForId =
- mUpdatedDrawablesForSource.get(drawableId);
if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
}
+ Map<String, Map<String, ParcelableResource>> drawablesForId =
+ mUpdatedDrawablesForSource.get(drawableId);
if (!drawablesForId.containsKey(drawableSource)) {
mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, new HashMap<>());
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b431b6c3ea5..dc2b6f8d3b64 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1459,8 +1459,9 @@ public final class SystemServer implements Dumpable {
Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
- if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
- Slog.e(TAG, "Unable to preload default resources");
+ String[] abis32 = Build.SUPPORTED_32_BIT_ABIS;
+ if (abis32.length > 0 && !Process.ZYGOTE_PROCESS.preloadDefault(abis32[0])) {
+ Slog.e(TAG, "Unable to preload default resources for secondary");
}
traceLog.traceEnd();
} catch (Exception ex) {
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 617321beadd2..cc64797578e7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -35,6 +35,8 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.os.Binder;
import android.os.Handler;
@@ -93,6 +95,11 @@ public class LocalDisplayAdapterTest {
@Mock
private LogicalLight mMockedBacklight;
+ @Mock
+ private DisplayManager mMockDisplayManager;
+ @Mock
+ private Point mMockDisplayStableSize;
+
private Handler mHandler;
private TestListener mListener = new TestListener();
@@ -123,6 +130,8 @@ public class LocalDisplayAdapterTest {
mListener, mInjector);
spyOn(mAdapter);
doReturn(mMockedContext).when(mAdapter).getOverlayContext();
+ doReturn(mMockDisplayManager).when(mMockedContext).getSystemService(DisplayManager.class);
+ doReturn(mMockDisplayStableSize).when(mMockDisplayManager).getStableDisplaySize();
TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS);
when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits))
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index c2cf2ff439ca..14f95e93577e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -107,7 +107,7 @@ public class ScribeTest {
@Test
public void testWriteHighLevelStateToDisk() {
long lastReclamationTime = System.currentTimeMillis();
- long remainingConsumableNarcs = 2000L;
+ long remainingConsumableCakes = 2000L;
long consumptionLimit = 500_000L;
when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit);
@@ -118,20 +118,20 @@ public class ScribeTest {
ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
- mScribeUnderTest.adjustRemainingConsumableNarcsLocked(
- remainingConsumableNarcs - consumptionLimit);
+ mScribeUnderTest.adjustRemainingConsumableCakesLocked(
+ remainingConsumableCakes - consumptionLimit);
assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
- assertEquals(remainingConsumableNarcs,
- mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(remainingConsumableCakes,
+ mScribeUnderTest.getRemainingConsumableCakesLocked());
assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
mScribeUnderTest.writeImmediatelyForTesting();
mScribeUnderTest.loadFromDiskLocked();
assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
- assertEquals(remainingConsumableNarcs,
- mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(remainingConsumableCakes,
+ mScribeUnderTest.getRemainingConsumableCakesLocked());
assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
}
@@ -234,32 +234,32 @@ public class ScribeTest {
@Test
public void testChangingConsumable() {
assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(0, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(0, mScribeUnderTest.getRemainingConsumableCakesLocked());
// Limit increased, so remaining value should be adjusted as well
mScribeUnderTest.setConsumptionLimitLocked(1000);
assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(1000, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(1000, mScribeUnderTest.getRemainingConsumableCakesLocked());
// Limit decreased below remaining, so remaining value should be adjusted as well
mScribeUnderTest.setConsumptionLimitLocked(500);
assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(500, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(500, mScribeUnderTest.getRemainingConsumableCakesLocked());
- mScribeUnderTest.adjustRemainingConsumableNarcsLocked(-100);
+ mScribeUnderTest.adjustRemainingConsumableCakesLocked(-100);
assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(400, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(400, mScribeUnderTest.getRemainingConsumableCakesLocked());
// Limit increased, so remaining value should be adjusted by the difference as well
mScribeUnderTest.setConsumptionLimitLocked(1000);
assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked());
// Limit decreased, but above remaining, so remaining value should left alone
mScribeUnderTest.setConsumptionLimitLocked(950);
assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
- assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+ assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked());
}
private void assertLedgersEqual(Ledger expected, Ledger actual) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index e4ee4d064724..fdf9354747a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -266,6 +266,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
public void sendIntentSender(IntentSender intent) {
// Placeholder for spying.
}
+
+ @Override
+ public String getPackageName() {
+ return SYSTEM_PACKAGE_NAME;
+ }
}
/** ShortcutService with injection override methods. */
@@ -704,6 +709,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected UriPermissionOwner mUriPermissionOwner;
+ protected static final String SYSTEM_PACKAGE_NAME = "android";
protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
protected static final int CALLING_UID_1 = 10001;
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index fbcad62988be..f2495e1545b5 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -18,6 +18,8 @@ package com.android.server.power;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -46,6 +48,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
import android.attention.AttentionManagerInternal;
import android.content.Context;
import android.content.ContextWrapper;
@@ -140,6 +143,7 @@ public class PowerManagerServiceTest {
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
@Mock private SystemPropertiesWrapper mSystemPropertiesMock;
+ @Mock private AppOpsManager mAppOpsManagerMock;
@Mock
private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@@ -297,6 +301,11 @@ public class PowerManagerServiceTest {
return new LowPowerStandbyController(context, mTestLooper.getLooper(),
SystemClock::elapsedRealtime);
}
+
+ @Override
+ AppOpsManager createAppOpsManager(Context context) {
+ return mAppOpsManagerMock;
+ }
});
return mService;
}
@@ -461,7 +470,7 @@ public class PowerManagerServiceTest {
}
@Test
- public void testWakefulnessAwake_AcquireCausesWakeup() {
+ public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() {
createService();
startSystem();
forceSleep();
@@ -469,6 +478,8 @@ public class PowerManagerServiceTest {
IBinder token = new Binder();
String tag = "acq_causes_wakeup";
String packageName = "pkg.name";
+ when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON,
+ Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED);
// First, ensure that a normal full wake lock does not cause a wakeup
int flags = PowerManager.FULL_WAKE_LOCK;
@@ -493,6 +504,27 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() {
+ createService();
+ startSystem();
+ forceSleep();
+
+ IBinder token = new Binder();
+ String tag = "acq_causes_wakeup";
+ String packageName = "pkg.name";
+ when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON,
+ Binder.getCallingUid(), packageName)).thenReturn(MODE_ERRORED);
+
+
+ // Verify that flag has no effect when OP_TURN_SCREEN_ON is not allowed
+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+ mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
+ }
+
+ @Test
public void testWakefulnessAwake_IPowerManagerWakeUp() {
createService();
startSystem();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 118f159bee7b..56244cb92a97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -854,7 +854,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
zeroRect};
final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
- displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
+ displayWidth, displayHeight, displayWidth, displayHeight, density, "",
+ Surface.ROTATION_0, 1f, 1f);
final DisplayCutout cutout = new WmDisplayCutout(
DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
@@ -874,7 +875,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
zeroRect};
final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
- displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
+ displayWidth, displayHeight, displayWidth, displayHeight, density, "",
+ Surface.ROTATION_90, 1f, 1f);
assertEquals(new WmDisplayCutout(
DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
.computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 04c52f7253e9..177e819878c7 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -16,6 +16,7 @@
package com.android.server.usb;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
@@ -28,10 +29,12 @@ import android.media.midi.MidiDeviceStatus;
import android.media.midi.MidiManager;
import android.media.midi.MidiReceiver;
import android.os.Bundle;
+import android.service.usb.UsbDirectMidiDeviceProto;
import android.util.Log;
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.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbEndpointDescriptor;
import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
@@ -52,6 +55,7 @@ public final class UsbDirectMidiDevice implements Closeable {
private static final boolean DEBUG = false;
private Context mContext;
+ private String mName;
private UsbDevice mUsbDevice;
private UsbDescriptorParser mParser;
private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
@@ -477,7 +481,7 @@ public final class UsbDirectMidiDevice implements Closeable {
} else {
name += " MIDI 1.0";
}
- Log.e(TAG, name);
+ mName = name;
properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -573,4 +577,21 @@ public final class UsbDirectMidiDevice implements Closeable {
}
return true;
}
+
+ /**
+ * Write a description of the device to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("num_inputs", UsbDirectMidiDeviceProto.NUM_INPUTS, mNumInputs);
+ dump.write("num_outputs", UsbDirectMidiDeviceProto.NUM_OUTPUTS, mNumOutputs);
+ dump.write("is_universal", UsbDirectMidiDeviceProto.IS_UNIVERSAL, mIsUniversalMidiDevice);
+ dump.write("name", UsbDirectMidiDeviceProto.NAME, mName);
+ if (mIsUniversalMidiDevice) {
+ mMidiBlockParser.dump(dump, "block_parser", UsbDirectMidiDeviceProto.BLOCK_PARSER);
+ }
+
+ dump.end(token);
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index dd58d3879bb2..b1c85fe043ec 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -588,6 +588,12 @@ public class UsbHostManager {
for (ConnectionRecord rec : mConnections) {
rec.dump(dump, "connections", UsbHostManagerProto.CONNECTIONS);
}
+
+ for (ArrayList<UsbDirectMidiDevice> directMidiDevices : mMidiDevices.values()) {
+ for (UsbDirectMidiDevice directMidiDevice : directMidiDevices) {
+ directMidiDevice.dump(dump, "midi_devices", UsbHostManagerProto.MIDI_DEVICES);
+ }
+ }
}
dump.end(token);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
index 37bd0f8f6f7b..eb083044c782 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
@@ -15,10 +15,15 @@
*/
package com.android.server.usb.descriptors;
+import android.annotation.NonNull;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDeviceConnection;
+import android.service.usb.UsbGroupTerminalBlockProto;
+import android.service.usb.UsbMidiBlockParserProto;
import android.util.Log;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
import java.util.ArrayList;
/**
@@ -70,6 +75,34 @@ public class UsbMidiBlockParser {
mMaxOutputBandwidth = stream.unpackUsbShort();
return mLength;
}
+
+ /**
+ * Write the state of the block to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("length", UsbGroupTerminalBlockProto.LENGTH, mLength);
+ dump.write("descriptor_type", UsbGroupTerminalBlockProto.DESCRIPTOR_TYPE,
+ mDescriptorType);
+ dump.write("descriptor_subtype", UsbGroupTerminalBlockProto.DESCRIPTOR_SUBTYPE,
+ mDescriptorSubtype);
+ dump.write("group_block_id", UsbGroupTerminalBlockProto.GROUP_BLOCK_ID, mGroupBlockId);
+ dump.write("group_terminal_block_type",
+ UsbGroupTerminalBlockProto.GROUP_TERMINAL_BLOCK_TYPE, mGroupTerminalBlockType);
+ dump.write("group_terminal", UsbGroupTerminalBlockProto.GROUP_TERMINAL,
+ mGroupTerminal);
+ dump.write("num_group_terminals", UsbGroupTerminalBlockProto.NUM_GROUP_TERMINALS,
+ mNumGroupTerminals);
+ dump.write("block_item", UsbGroupTerminalBlockProto.BLOCK_ITEM, mBlockItem);
+ dump.write("midi_protocol", UsbGroupTerminalBlockProto.MIDI_PROTOCOL, mMidiProtocol);
+ dump.write("max_input_bandwidth", UsbGroupTerminalBlockProto.MAX_INPUT_BANDWIDTH,
+ mMaxInputBandwidth);
+ dump.write("max_output_bandwidth", UsbGroupTerminalBlockProto.MAX_OUTPUT_BANDWIDTH,
+ mMaxOutputBandwidth);
+
+ dump.end(token);
+ }
}
private ArrayList<GroupTerminalBlock> mGroupTerminalBlocks =
@@ -170,4 +203,24 @@ public class UsbMidiBlockParser {
}
return DEFAULT_MIDI_TYPE;
}
+
+ /**
+ * Write the state of the parser to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("length", UsbMidiBlockParserProto.LENGTH, mHeaderLength);
+ dump.write("descriptor_type", UsbMidiBlockParserProto.DESCRIPTOR_TYPE,
+ mHeaderDescriptorType);
+ dump.write("descriptor_subtype", UsbMidiBlockParserProto.DESCRIPTOR_SUBTYPE,
+ mHeaderDescriptorSubtype);
+ dump.write("total_length", UsbMidiBlockParserProto.TOTAL_LENGTH, mTotalLength);
+ for (GroupTerminalBlock groupTerminalBlock : mGroupTerminalBlocks) {
+ groupTerminalBlock.dump(dump, "block", UsbMidiBlockParserProto.BLOCK);
+ }
+
+ dump.end(token);
+ }
+
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9c886aada728..09d3a0d6392b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -176,7 +176,7 @@ public class CarrierConfigManager {
* This flag specifies whether VoLTE availability is based on provisioning. By default this is
* false.
* Used for UCE to determine if EAB provisioning checks should be based on provisioning.
- * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead.
+ * @deprecated Use {@link Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL} instead.
*/
@Deprecated
public static final String
@@ -2901,11 +2901,11 @@ public class CarrierConfigManager {
* <p>
* 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-140, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, -44]"</LI>
+ * <LI>"NONE: [-140, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, -44]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -2921,11 +2921,11 @@ public class CarrierConfigManager {
* <p>
* 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-43, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, 20]"</LI>
+ * <LI>"NONE: [-43, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, 20]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -2942,11 +2942,11 @@ public class CarrierConfigManager {
* <p>
* 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-23, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, 40]"</LI>
+ * <LI>"NONE: [-23, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, 40]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index f5ba3abf53a5..38becc6af0dc 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -438,13 +438,13 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
int level;
if (measure == CellInfo.UNAVAILABLE) {
level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- } else if (measure > thresholds[3]) {
+ } else if (measure >= thresholds[3]) {
level = SIGNAL_STRENGTH_GREAT;
- } else if (measure > thresholds[2]) {
+ } else if (measure >= thresholds[2]) {
level = SIGNAL_STRENGTH_GOOD;
- } else if (measure > thresholds[1]) {
+ } else if (measure >= thresholds[1]) {
level = SIGNAL_STRENGTH_MODERATE;
- } else if (measure > thresholds[0]) {
+ } else if (measure >= thresholds[0]) {
level = SIGNAL_STRENGTH_POOR;
} else {
level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 34158685b6d1..e00ea0ea4d5e 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -18,6 +18,7 @@ package android.telephony.ims;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -43,6 +44,8 @@ import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ITelephony;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -87,6 +90,46 @@ public class ImsRcsManager {
"android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
/**
+ * This carrier supports User Capability Exchange as, defined by the framework using a
+ * presence server. If set, the RcsFeature should support capability exchange. If not set, this
+ * RcsFeature should not publish capabilities or service capability requests.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+ CAPABILITY_TYPE_NONE,
+ CAPABILITY_TYPE_OPTIONS_UCE,
+ CAPABILITY_TYPE_PRESENCE_UCE
+ })
+ public @interface RcsImsCapabilityFlag {}
+
+ /**
+ * Undefined capability type for initialization
+ */
+ public static final int CAPABILITY_TYPE_NONE = 0;
+
+ /**
+ * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+ * If not set, this RcsFeature should not service capability requests.
+ */
+ public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+ /**
+ * This carrier supports User Capability Exchange using a presence server as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using a presence
+ * server. If not set, this RcsFeature should not publish capabilities or service capability
+ * requests using presence.
+ */
+ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+ /**
+ * This is used to check the upper range of RCS capability
+ * @hide
+ */
+ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+ /**
* An application can use {@link #addOnAvailabilityChangedListener} to register a
* {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
* availability status updates from the ImsService.
@@ -104,7 +147,7 @@ public class ImsRcsManager {
*
* @param capabilities The new availability of the capabilities.
*/
- void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+ void onAvailabilityChanged(@RcsImsCapabilityFlag int capabilities);
}
/**
@@ -486,7 +529,7 @@ public class ImsRcsManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+ public boolean isCapable(@RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
@@ -522,7 +565,7 @@ public class ImsRcsManager {
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+ public boolean isAvailable(@RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 677c1a9a7d76..2833489d654c 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -38,7 +38,6 @@ import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -970,7 +969,7 @@ public class ProvisioningManager {
/**
* Callback for IMS provisioning feature changes.
*/
- public static class FeatureProvisioningCallback {
+ public abstract static class FeatureProvisioningCallback {
private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
@@ -1024,12 +1023,10 @@ public class ProvisioningManager {
* specified, or {@code false} if the capability is not provisioned for the technology
* specified.
*/
- public void onFeatureProvisioningChanged(
+ public abstract void onFeatureProvisioningChanged(
@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech,
- boolean isProvisioned) {
- // Base Implementation
- }
+ boolean isProvisioned);
/**
* The IMS RCS provisioning has changed for a specific capability and IMS
@@ -1041,12 +1038,10 @@ public class ProvisioningManager {
* specified, or {@code false} if the capability is not provisioned for the technology
* specified.
*/
- public void onRcsFeatureProvisioningChanged(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ public abstract void onRcsFeatureProvisioningChanged(
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech,
- boolean isProvisioned) {
- // Base Implementation
- }
+ boolean isProvisioned);
/**@hide*/
public final IFeatureProvisioningCallback getBinder() {
@@ -1505,7 +1500,7 @@ public class ProvisioningManager {
* Get the provisioning status for the IMS RCS capability specified.
*
* If provisioning is not required for the queried
- * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
+ * {@link ImsRcsManager.RcsImsCapabilityFlag} this method will always return
* {@code true}.
*
* @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
@@ -1522,7 +1517,7 @@ public class ProvisioningManager {
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean getRcsProvisioningStatusForCapability(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ @ImsRcsManager.RcsImsCapabilityFlag int capability) {
try {
return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
@@ -1535,7 +1530,7 @@ public class ProvisioningManager {
* Get the provisioning status for the IMS RCS capability specified.
*
* If provisioning is not required for the queried
- * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method
+ * {@link ImsRcsManager.RcsImsCapabilityFlag} this method
* will always return {@code true}.
*
* <p> Requires Permission:
@@ -1553,7 +1548,7 @@ public class ProvisioningManager {
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public boolean getRcsProvisioningStatusForCapability(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech) {
try {
return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
@@ -1590,7 +1585,7 @@ public class ProvisioningManager {
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRcsProvisioningStatusForCapability(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
boolean isProvisioned) {
try {
getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
@@ -1622,7 +1617,7 @@ public class ProvisioningManager {
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRcsProvisioningStatusForCapability(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
try {
getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
@@ -1676,7 +1671,7 @@ public class ProvisioningManager {
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public boolean isRcsProvisioningRequiredForCapability(
- @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech) {
try {
return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 154bb11db5e3..91dc38ff9ddb 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -55,20 +55,28 @@ public class RcsUceAdapter {
* This carrier supports User Capability Exchange as, defined by the framework using
* SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this
* RcsFeature should not publish capabilities or service capability requests.
+ * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead.
* @hide
*/
+ @Deprecated
public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
/**
* This carrier supports User Capability Exchange as, defined by the framework using a
* presence server. If set, the RcsFeature should support capability exchange. If not set, this
* RcsFeature should not publish capabilities or service capability requests.
+ * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
- /**@hide*/
+ /**
+ * @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead.
+ * @hide
+ */
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "CAPABILITY_TYPE_", value = {
CAPABILITY_TYPE_OPTIONS_UCE,
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index ad2e9e133a11..fb0e659ec77b 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -396,7 +396,7 @@ public class MmTelFeature extends ImsFeature {
/**
* Undefined capability type for initialization
* This is used to check the upper range of MmTel capability
- * {@hide}
+ * @hide
*/
public static final int CAPABILITY_TYPE_NONE = 0;
@@ -427,7 +427,7 @@ public class MmTelFeature extends ImsFeature {
/**
* This is used to check the upper range of MmTel capability
- * {@hide}
+ * @hide
*/
public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
@@ -738,7 +738,7 @@ public class MmTelFeature extends ImsFeature {
* Enabling/Disabling a capability here indicates that the capability should be registered or
* deregistered (depending on the capability change) and become available or unavailable to
* the framework.
- * * @hide
+ * @hide
*/
@Override
@SystemApi
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 70e4ef1f1a3a..843827befb65 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -24,7 +24,7 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.net.Uri;
import android.os.RemoteException;
-import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper;
import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -59,7 +59,9 @@ import java.util.function.Supplier;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
* this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
*/
+@SystemApi
public class RcsFeature extends ImsFeature {
private static final String LOG_TAG = "RcsFeature";
@@ -184,18 +186,22 @@ public class RcsFeature extends ImsFeature {
* Contains the capabilities defined and supported by a {@link RcsFeature} in the
* form of a bitmask. The capabilities that are used in the RcsFeature are
* defined as:
- * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
- * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+ * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+ * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
*
* The enabled capabilities of this RcsFeature will be set by the framework
- * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+ * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
* After the capabilities have been set, the RcsFeature may then perform the necessary bring up
* of the capability and notify the capability status as true using
- * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
*/
public static class RcsImsCapabilities extends Capabilities {
- /** @hide*/
+
+ /**
+ * Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead in case used for public API
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
CAPABILITY_TYPE_NONE,
@@ -226,7 +232,7 @@ public class RcsFeature extends ImsFeature {
/**
* This is used to check the upper range of RCS capability
- * {@hide}
+ * @hide
*/
public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
@@ -234,10 +240,8 @@ public class RcsFeature extends ImsFeature {
* Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for RCS in the form of a
* bitfield.
- * @hide
*/
- @SystemApi
- public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ public RcsImsCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
@@ -249,30 +253,18 @@ public class RcsFeature extends ImsFeature {
super(capabilities.getMask());
}
- /**
- * @hide
- */
@Override
- @SystemApi
- public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ public void addCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
super.addCapabilities(capabilities);
}
- /**
- * @hide
- */
@Override
- @SystemApi
- public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ public void removeCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
super.removeCapabilities(capabilities);
}
- /**
- * @hide
- */
@Override
- @SystemApi
- public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ public boolean isCapable(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
return super.isCapable(capabilities);
}
}
@@ -288,9 +280,7 @@ public class RcsFeature extends ImsFeature {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
- * @hide
*/
- @SystemApi
public RcsFeature() {
super();
// Run on the Binder threads that call them.
@@ -302,9 +292,7 @@ public class RcsFeature extends ImsFeature {
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of RcsFeature.
- * @hide
*/
- @SystemApi
public RcsFeature(@NonNull Executor executor) {
super();
if (executor == null) {
@@ -335,10 +323,8 @@ public class RcsFeature extends ImsFeature {
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
* @return A copy of the current RcsFeature capability status.
- * @hide
*/
@Override
- @SystemApi
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
return new RcsImsCapabilities(super.queryCapabilityStatus());
}
@@ -348,9 +334,7 @@ public class RcsFeature extends ImsFeature {
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
* @param capabilities The current capability status of the RcsFeature.
- * @hide
*/
- @SystemApi
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
if (capabilities == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
@@ -367,11 +351,9 @@ public class RcsFeature extends ImsFeature {
* @param capability The capability that we are querying the configuration for.
* @param radioTech The radio technology type that we are querying.
* @return true if the capability is enabled, false otherwise.
- * @hide
*/
- @SystemApi
public boolean queryCapabilityConfiguration(
- @RcsUceAdapter.RcsImsCapabilityFlag int capability,
+ @ImsRcsManager.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base Implementation - Override to provide functionality
return false;
@@ -392,10 +374,8 @@ public class RcsFeature extends ImsFeature {
* be called for each capability change that resulted in an error.
* @param request The request to change the capability.
* @param callback To notify the framework that the result of the capability changes.
- * @hide
*/
@Override
- @SystemApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy callback) {
// Base Implementation - Override to provide functionality
@@ -415,9 +395,7 @@ public class RcsFeature extends ImsFeature {
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
- * @hide
*/
- @SystemApi
public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
@NonNull CapabilityExchangeEventListener listener) {
// Base Implementation, override to implement functionality
@@ -427,28 +405,20 @@ public class RcsFeature extends ImsFeature {
/**
* Remove the given CapabilityExchangeImplBase instance.
* @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
- * @hide
*/
- @SystemApi
public void destroyCapabilityExchangeImpl(
@NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
// Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
}
- /**{@inheritDoc}
- * @hide
- */
+ /**{@inheritDoc}*/
@Override
- @SystemApi
public void onFeatureRemoved() {
}
- /**{@inheritDoc}
- * @hide
- */
+ /**{@inheritDoc}*/
@Override
- @SystemApi
public void onFeatureReady() {
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index ac5565bea810..593f0807ef27 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -93,7 +93,7 @@ public class ImsRegistrationImplBase {
/**
* This is used to check the upper range of registration tech
- * {@hide}
+ * @hide
*/
public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1;
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 75f1337b9388..7f309e1974e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -102,28 +102,59 @@ fun FlickerTestParameter.statusBarLayerIsVisible() {
}
}
-fun FlickerTestParameter.navBarLayerRotatesAndScales() {
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
+ * of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerPositionStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display))
}
+}
+
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the end
+ * of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerPositionEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display))
}
}
-fun FlickerTestParameter.statusBarLayerRotatesScales() {
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
+ * and end of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerRotatesAndScales() {
+ navBarLayerPositionStart()
+ navBarLayerPositionEnd()
+}
+
+/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerPositionStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
+}
+
+/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the end
+ * of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerPositionEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
@@ -133,6 +164,15 @@ fun FlickerTestParameter.statusBarLayerRotatesScales() {
}
/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * and end of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerRotatesScales() {
+ statusBarLayerPositionStart()
+ statusBarLayerPositionEnd()
+}
+
+/**
* Asserts that:
* [originalLayer] is visible at the start of the trace
* [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
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 d1319ac095ed..20a2e2228d7a 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
@@ -98,6 +98,12 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
super.statusBarLayerRotatesScales()
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229762973)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
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 f5fb10611d6b..d00fd7d7f09b 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
@@ -101,11 +101,10 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
}
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 229762973)
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
companion object {
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 535612a2bd8b..93b987ea8787 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -41,6 +41,16 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
waitIMEShown(device, wmHelper)
}
+ override fun launchViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ expectedWindowName: String,
+ action: String?,
+ stringExtras: Map<String, String>
+ ) {
+ super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
+ waitIMEShown(uiDevice, wmHelper)
+ }
+
override fun open() {
val expectedPackage = if (rotation.isRotated()) {
imePackageName
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 8daf4ca696bf..a8cbc5dc922c 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
@@ -18,12 +18,14 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -31,6 +33,8 @@ import org.junit.runners.Parameterized
/**
* Test cold launching an app from a notification from the lock screen.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold`
*/
@RequiresDevice
@@ -65,6 +69,22 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter)
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
companion object {
/**
* Creates the test configurations.
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 8eb182a9fa31..cd8dea012db5 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
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -33,6 +34,8 @@ import org.junit.runners.Parameterized
/**
* Test warm launching an app from a notification from the lock screen.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm`
*/
@RequiresDevice
@@ -97,6 +100,17 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter)
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
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 28a914b96449..bc637f8f9a63 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
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -103,6 +104,11 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
/**
* Creates the test configurations.
@@ -113,7 +119,7 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
- return com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
+ return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 3)
}
}
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 f47e2726e6ad..fe80162b5b81 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
@@ -20,6 +20,7 @@ import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.navBarLayerPositionEnd
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
@@ -99,4 +100,27 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter)
@FlakyTest(bugId = 203538234)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ *
+ * Differently from the normal usage of this assertion, check only the final state of the
+ * transition because the display is off at the start and the NavBar is never visible
+ */
+ @Presubmit
+ @Test
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
+
+ /**
+ * Checks that the status bar layer is visible at the end of the trace
+ *
+ * It is not possible to check at the start because the screen is off
+ */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() {
+ testSpec.assertLayersEnd {
+ this.isVisible(FlickerComponentName.STATUS_BAR)
+ }
+ }
}
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 ee018ecfd25a..5022dd8f9bff 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
@@ -32,6 +32,8 @@ import org.junit.runners.Parameterized
/**
* Test cold launching an app from a notification.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
*/
@RequiresDevice
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 74f1fd781a51..812f6859c7b0 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
@@ -20,6 +20,7 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
import android.view.WindowInsets
import android.view.WindowManager
+import androidx.test.filters.FlakyTest
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -29,8 +30,10 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NotificationAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,6 +43,8 @@ import org.junit.runners.Parameterized
/**
* Test cold launching an app from a notification.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
*/
@RequiresDevice
@@ -160,6 +165,21 @@ open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter)
}
}
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesTopWindow() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ super.appWindowBecomesTopWindow()
+ }
+
+ @FlakyTest(bugId = 229738092)
+ @Test
+ fun appWindowBecomesTopWindow_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ super.appWindowBecomesTopWindow()
+ }
+
companion object {
/**
* Creates the test configurations.
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 6e33f66d111d..2226fd1d2155 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
@@ -26,9 +26,11 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -132,6 +134,41 @@ open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ @FlakyTest(bugId = 229738092)
+ @Test
+ fun appWindowReplacesLauncherAsTopWindow_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowBecomesTopWindow() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ super.appWindowBecomesTopWindow()
+ }
+
+ @FlakyTest(bugId = 229738092)
+ @Test
+ fun appWindowBecomesTopWindow_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ super.appWindowBecomesTopWindow()
+ }
+
companion object {
/**
* Creates the test configurations.
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 f357177aade2..55eb3c3d1a0a 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
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Postsubmit
import androidx.test.filters.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
@@ -27,6 +28,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerPositionEnd
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -37,6 +39,8 @@ import org.junit.runners.Parameterized
/**
* Test launching an app while the device is locked
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
*
* Actions:
@@ -103,8 +107,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
/**
* Checks that the status bar layer is visible at the end of the trace
*
- * It is not possible to check at the start because the animation is working differently
- * in devices with and without blur (b/202936526)
+ * It is not possible to check at the start because the screen is off
*/
@Presubmit
@Test
@@ -115,7 +118,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 202936526)
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@@ -131,10 +134,15 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
}
}
- /** {@inheritDoc} */
- @FlakyTest
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ *
+ * Differently from the normal usage of this assertion, check only the final state of the
+ * transition because the display is off at the start and the NavBar is never visible
+ */
+ @Postsubmit
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
/** {@inheritDoc} */
@FlakyTest
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 48f6aeb4d824..97528c0471cc 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
@@ -130,6 +130,10 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/TrustTests/TEST_MAPPING b/tests/TrustTests/TEST_MAPPING
index b9c46bfbba8c..23923eeb83ee 100644
--- a/tests/TrustTests/TEST_MAPPING
+++ b/tests/TrustTests/TEST_MAPPING
@@ -11,5 +11,18 @@
}
]
}
+ ],
+ "trust-tablet": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
} \ No newline at end of file
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 5f606e1dab0c..09080be9ee41 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -26,6 +26,7 @@ import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -50,9 +51,11 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
@@ -104,6 +107,26 @@ public class TelephonySubscriptionTrackerTest {
TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
+ private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1";
+ private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2";
+ private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle();
+ private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER;
+ private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP;
+
+ static {
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG);
+
+ final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
+ subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER);
+ TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
+ }
+
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@NonNull private final Handler mHandler;
@@ -144,6 +167,9 @@ public class TelephonySubscriptionTrackerTest {
doReturn(mCarrierConfigManager)
.when(mContext)
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ doReturn(TEST_CARRIER_CONFIG)
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
// subId 1, 2 are in same subGrp, only subId 1 is active
doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -227,14 +253,24 @@ public class TelephonySubscriptionTrackerTest {
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
int activeSubId,
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(
+ activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages);
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ int activeSubId,
+ Map<Integer, SubscriptionInfo> subIdToInfoMap,
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
+ Map<ParcelUuid, Set<String>> privilegedPackages) {
+ return new TelephonySubscriptionSnapshot(
+ activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages);
}
private void verifyNoActiveSubscriptions() {
@@ -245,6 +281,8 @@ public class TelephonySubscriptionTrackerTest {
private void setupReadySubIds() {
mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER));
}
private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
@@ -300,6 +338,7 @@ public class TelephonySubscriptionTrackerTest {
readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP);
doReturn(1).when(mTelephonyManager).getActiveModemCount();
List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks =
@@ -464,8 +503,16 @@ public class TelephonySubscriptionTrackerTest {
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
mTestLooper.dispatchAll();
- verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
+ verify(mCallback)
+ .onNewSnapshot(
+ eq(
+ buildExpectedSnapshot(
+ 0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap())));
assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+ assertNull(
+ mTelephonySubscriptionTracker
+ .getSubIdToCarrierConfigMap()
+ .get(TEST_SUBSCRIPTION_ID_1));
}
@Test
@@ -493,7 +540,7 @@ public class TelephonySubscriptionTrackerTest {
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -503,7 +550,7 @@ public class TelephonySubscriptionTrackerTest {
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 841b81c2a282..15d4f1097108 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -56,6 +56,7 @@ import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -216,14 +217,23 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test
public void testMigration() throws Exception {
triggerChildOpened();
+ mTestLooper.dispatchAll();
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
mGatewayConnection
.getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+
+ final IkeSessionConnectionInfo newIkeConnectionInfo =
+ new IkeSessionConnectionInfo(
+ TEST_ADDR_V4, TEST_ADDR_V4_2, TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ getIkeSessionCallback().onIkeSessionConnectionInfoChanged(newIkeConnectionInfo);
getChildSessionCallback()
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
+ assertEquals(newIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+
verify(mIpSecSvc, times(2))
.setNetworkForTunnelInterface(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
@@ -246,7 +256,8 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
MtuUtils.getMtu(
saProposals,
mConfig.getMaxMtu(),
- TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu());
+ TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu(),
+ true /* isIpv4 */);
verify(mNetworkAgent).sendLinkProperties(
argThat(lp -> expectedMtu == lp.getMtu()
&& TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes())));
@@ -269,6 +280,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
.when(mMockChildSessionConfig)
.getInternalDnsServers();
+ getIkeSessionCallback().onOpened(mIkeSessionConfiguration);
getChildSessionCallback().onOpened(mMockChildSessionConfig);
}
@@ -298,6 +310,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
final ArgumentCaptor<LinkProperties> lpCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index b9dfda38a01c..6a9a1e22cab1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -213,7 +213,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
@@ -226,7 +227,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 5628321b5975..785bff167ad2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -47,6 +47,8 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -80,6 +82,13 @@ public class VcnGatewayConnectionTestBase {
doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
}
+ protected static final InetAddress TEST_ADDR = InetAddresses.parseNumericAddress("2001:db8::1");
+ protected static final InetAddress TEST_ADDR_2 =
+ InetAddresses.parseNumericAddress("2001:db8::2");
+ protected static final InetAddress TEST_ADDR_V4 =
+ InetAddresses.parseNumericAddress("192.0.2.1");
+ protected static final InetAddress TEST_ADDR_V4_2 =
+ InetAddresses.parseNumericAddress("192.0.2.2");
protected static final InetAddress TEST_DNS_ADDR =
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -129,6 +138,7 @@ public class VcnGatewayConnectionTestBase {
new TelephonySubscriptionSnapshot(
TEST_SUB_ID,
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO),
+ Collections.EMPTY_MAP,
Collections.EMPTY_MAP);
@NonNull protected final Context mContext;
@@ -148,6 +158,9 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
+ @NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
+
protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
@@ -173,6 +186,10 @@ public class VcnGatewayConnectionTestBase {
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mIkeConnectionInfo =
+ new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
+ mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
+
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 6c849b5af888..b0d68952c39d 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -30,6 +30,7 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.ch
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -309,7 +310,9 @@ public class NetworkPriorityClassifierTest {
wifiNetworkPriority,
mWifiNetworkRecord,
selectedNetworkRecord,
- carrierConfig));
+ carrierConfig == null
+ ? null
+ : new PersistableBundleWrapper(carrierConfig)));
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
index 29511f780bf6..e9e70783ebe9 100644
--- a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -46,34 +46,85 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MtuUtilsTest {
+ private void verifyUnderlyingMtuZero(boolean isIpv4) {
+ assertEquals(
+ IPV6_MIN_MTU,
+ getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */, isIpv4));
+ }
+
+ @Test
+ public void testUnderlyingMtuZeroV4() {
+ verifyUnderlyingMtuZero(true /* isIpv4 */);
+ }
+
@Test
- public void testUnderlyingMtuZero() {
+ public void testUnderlyingMtuZeroV6() {
+ verifyUnderlyingMtuZero(false /* isIpv4 */);
+ }
+
+ private void verifyClampsToMaxMtu(boolean isIpv4) {
assertEquals(
- IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+ 0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */, isIpv4));
}
@Test
- public void testClampsToMaxMtu() {
- assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+ public void testClampsToMaxMtuV4() {
+ verifyClampsToMaxMtu(true /* isIpv4 */);
}
@Test
- public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
- final List<ChildSaProposal> saProposals =
- Arrays.asList(
- new ChildSaProposal.Builder()
- .addEncryptionAlgorithm(
- ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
- .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
- .build());
+ public void testClampsToMaxMtuV6() {
+ verifyClampsToMaxMtu(false /* isIpv4 */);
+ }
+
+ private List<ChildSaProposal> buildChildSaProposalsWithNormalModeAlgo() {
+ return Arrays.asList(
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .build());
+ }
+ private void verifyNormalModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
@Test
- public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
+
+ @Test
+ public void testMtuIpv4LessThanMtuIpv6() {
+ final int actualMtuV4 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ true /* isIpv4 */);
+
+ final int actualMtuV6 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ false /* isIpv4 */);
+
+ assertTrue(actualMtuV4 < actualMtuV6);
+ }
+
+ private void verifyCombinedModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final List<ChildSaProposal> saProposals =
Arrays.asList(
new ChildSaProposal.Builder()
@@ -86,7 +137,17 @@ public class MtuUtilsTest {
.build());
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */, isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
index a44a734a2dce..294f5c1f4842 100644
--- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
@@ -18,6 +18,8 @@ package com.android.server.vcn.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.os.PersistableBundle;
@@ -211,4 +213,84 @@ public class PersistableBundleUtilsTest {
assertEquals(testInt, result);
}
+
+ private PersistableBundle getTestBundle() {
+ final PersistableBundle bundle = new PersistableBundle();
+
+ bundle.putBoolean(TEST_KEY + "Boolean", true);
+ bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false});
+ bundle.putDouble(TEST_KEY + "Double", 0.1);
+ bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3});
+ bundle.putInt(TEST_KEY + "Int", 1);
+ bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2});
+ bundle.putLong(TEST_KEY + "Long", 5L);
+ bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L});
+ bundle.putString(TEST_KEY + "String", "TEST");
+ bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"});
+ bundle.putPersistableBundle(
+ TEST_KEY + "PersistableBundle",
+ new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle())
+ .toPersistableBundle());
+
+ return bundle;
+ }
+
+ @Test
+ public void testMinimizeBundle() throws Exception {
+ final String[] minimizedKeys =
+ new String[] {
+ TEST_KEY + "Boolean",
+ TEST_KEY + "BooleanArray",
+ TEST_KEY + "Double",
+ TEST_KEY + "DoubleArray",
+ TEST_KEY + "Int",
+ TEST_KEY + "IntArray",
+ TEST_KEY + "Long",
+ TEST_KEY + "LongArray",
+ TEST_KEY + "String",
+ TEST_KEY + "StringArray",
+ TEST_KEY + "PersistableBundle"
+ };
+
+ final PersistableBundle testBundle = getTestBundle();
+ testBundle.putBoolean(TEST_KEY + "Boolean2", true);
+
+ final PersistableBundle minimized =
+ PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys);
+
+ // Verify that the minimized bundle is NOT the same in size OR values due to the extra
+ // Boolean2 key
+ assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized));
+
+ // Verify that removing the extra key from the source bundle results in equality.
+ testBundle.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized));
+ }
+
+ @Test
+ public void testEquality_identical() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_different() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ left.putBoolean(TEST_KEY + "Boolean2", true);
+ assertFalse(PersistableBundleUtils.isEqual(left, right));
+
+ left.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_null() throws Exception {
+ assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null));
+ assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle()));
+ assertTrue(PersistableBundleUtils.isEqual(null, null));
+ }
}