summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ProtoLibraries.bp18
-rw-r--r--core/api/current.txt24
-rw-r--r--core/api/module-lib-current.txt38
-rw-r--r--core/api/system-current.txt8
-rw-r--r--core/api/test-current.txt26
-rw-r--r--core/java/android/app/ActivityOptions.java14
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java71
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java149
-rw-r--r--core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java45
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl9
-rw-r--r--core/java/android/app/admin/PreferentialNetworkServiceConfig.java6
-rw-r--r--core/java/android/app/servertransaction/ActivityTransactionItem.java4
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java7
-rw-r--r--core/java/android/app/servertransaction/StartActivityItem.java4
-rw-r--r--core/java/android/app/trust/TEST_MAPPING15
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl1
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl11
-rw-r--r--core/java/android/content/pm/PackageInstaller.java25
-rw-r--r--core/java/android/content/pm/PackageManager.java11
-rw-r--r--core/java/android/content/pm/PackageParser.java31
-rw-r--r--core/java/android/hardware/CameraSessionStats.java11
-rw-r--r--core/java/android/hardware/CameraStreamStats.java13
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java29
-rw-r--r--core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl7
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java36
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java19
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java99
-rw-r--r--core/java/android/nfc/Tag.java10
-rw-r--r--core/java/android/os/ExternalVibration.java10
-rw-r--r--core/java/android/os/IpcDataCache.java364
-rw-r--r--core/java/android/os/Process.java10
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl12
-rw-r--r--core/java/android/os/storage/StorageManager.java13
-rw-r--r--core/java/android/service/dreams/DreamService.java4
-rw-r--r--core/java/android/service/dreams/OWNERS5
-rw-r--r--core/java/android/service/games/GameScreenshotResult.java52
-rw-r--r--core/java/android/service/games/GameSession.java13
-rw-r--r--core/java/android/view/IWindowManager.aidl5
-rw-r--r--core/java/android/view/InputWindowHandle.java101
-rw-r--r--core/java/android/view/MotionEvent.java2
-rw-r--r--core/java/android/view/ViewConfiguration.java1
-rw-r--r--core/java/android/view/WindowManager.java57
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java32
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java19
-rw-r--r--core/java/android/widget/RemoteViews.java8
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl2
-rw-r--r--core/java/android/window/TaskOrganizer.java2
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java46
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java49
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java7
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java71
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl8
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp104
-rw-r--r--core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp4
-rw-r--r--core/proto/android/os/incident.proto4
-rw-r--r--core/proto/android/service/netstats.proto121
-rw-r--r--core/res/AndroidManifest.xml34
-rw-r--r--core/res/res/drawable/grant_permissions_buttons_bottom.xml23
-rw-r--r--core/res/res/drawable/grant_permissions_buttons_top.xml23
-rw-r--r--core/res/res/layout/log_access_user_consent_dialog_permission.xml86
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/ids.xml16
-rw-r--r--core/res/res/values/public-final.xml (renamed from core/res/res/values/public.xml)145
-rw-r--r--core/res/res/values/public-staging.xml228
-rw-r--r--core/res/res/values/strings.xml31
-rw-r--r--core/res/res/values/styles.xml38
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/res/raw/obb_enc_file100_orig1.obbbin275008 -> 0 bytes
-rw-r--r--core/tests/coretests/res/raw/obb_enc_file100_orig3.obbbin298560 -> 0 bytes
-rw-r--r--core/tests/coretests/src/android/os/IpcDataCacheTest.java312
-rw-r--r--core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java26
-rw-r--r--core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java59
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java24
-rw-r--r--data/etc/privapp-permissions-platform.xml21
-rw-r--r--graphics/java/android/graphics/RuntimeShader.java220
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java)121
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java)30
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java60
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java55
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java74
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java96
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java36
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java33
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java150
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java98
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java2
-rw-r--r--libs/WindowManager/Shell/res/color/letterbox_education_dismiss_button_background_ripple.xml (renamed from packages/SystemUI/res/layout/communal_host_view.xml)12
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/strings_tv.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java88
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java111
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java2
-rw-r--r--libs/storage/IMountService.cpp3
-rw-r--r--libs/storage/include/storage/IMountService.h4
-rw-r--r--media/java/android/media/AudioManager.java16
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl3
-rw-r--r--media/java/android/media/MediaRouter2.java892
-rw-r--r--media/java/android/media/NearbyDevice.java38
-rw-r--r--media/java/android/media/tv/interactive/TvInteractiveAppInfo.java4
-rw-r--r--native/android/storage_manager.cpp13
-rw-r--r--packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml22
-rw-r--r--packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml22
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_apps.xml26
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_notifications.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_storage.xml25
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml73
-rw-r--r--packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml53
-rw-r--r--packages/CompanionDeviceManager/res/layout/helper_confirmation.xml2
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_device.xml18
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_permission.xml55
-rw-r--r--packages/CompanionDeviceManager/res/values-night/themes.xml26
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values/themes.xml3
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java72
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java19
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java128
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java7
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java10
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java14
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java16
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java26
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java4
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java16
-rw-r--r--packages/SettingsLib/res/drawable/ic_account_circle.xml (renamed from packages/SystemUI/res/drawable/ic_account_circle.xml)0
-rw-r--r--packages/SettingsLib/res/drawable/ic_account_circle_filled.xml (renamed from packages/SystemUI/res/drawable/ic_account_circle_filled.xml)0
-rw-r--r--packages/SettingsLib/res/drawable/ic_add_supervised_user.xml (renamed from packages/SystemUI/res/drawable/ic_add_supervised_user.xml)0
-rw-r--r--packages/SettingsLib/res/drawable/user_avatar_bg.xml (renamed from packages/SystemUI/res/drawable/kg_bg_avatar.xml)2
-rw-r--r--packages/SettingsLib/res/values/colors.xml3
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java12
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java17
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java13
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java2
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt50
-rw-r--r--packages/SystemUI/proguard.flags8
-rw-r--r--packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml3
-rw-r--r--packages/SystemUI/res/color/kg_user_avatar_frame.xml4
-rw-r--r--packages/SystemUI/res/drawable/user_switcher_icon_large.xml2
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml4
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml2
-rw-r--r--packages/SystemUI/res/layout/screenshot_static.xml14
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml48
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java87
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButton.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java51
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java396
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java158
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java180
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java125
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/PackageObserver.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java125
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt219
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt351
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java354
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java237
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt152
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java233
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java242
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java205
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/PackageObserverTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt172
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt181
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java9
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/TransportManager.java54
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java57
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java7
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java24
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java51
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/AppBatteryTracker.java6
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java1
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java44
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java3
-rw-r--r--services/core/java/com/android/server/am/PendingStartActivityUids.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java4
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java33
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java12
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java15
-rw-r--r--services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java37
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java17
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java303
-rw-r--r--services/core/java/com/android/server/infra/AbstractPerUserSystemService.java123
-rw-r--r--services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java139
-rw-r--r--services/core/java/com/android/server/infra/ServiceNameResolver.java71
-rw-r--r--services/core/java/com/android/server/input/GestureMonitorSpyWindow.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java18
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java14
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java96
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java4
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodUtils.java40
-rw-r--r--services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java159
-rw-r--r--services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java130
-rw-r--r--services/core/java/com/android/server/logcat/LogAccessDialogActivity.java169
-rw-r--r--services/core/java/com/android/server/logcat/LogcatManagerService.java229
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java44
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java29
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java6
-rw-r--r--services/core/java/com/android/server/notification/ZenModeFiltering.java69
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java5
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java21
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java2
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/Computer.java15
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java42
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/DumpHelper.java25
-rw-r--r--services/core/java/com/android/server/pm/DynamicCodeLoggingService.java6
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java102
-rw-r--r--services/core/java/com/android/server/pm/InstallParams.java4
-rw-r--r--services/core/java/com/android/server/pm/Installer.java6
-rw-r--r--services/core/java/com/android/server/pm/IntentResolverInterceptor.java126
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java53
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerNative.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5172
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java49
-rw-r--r--services/core/java/com/android/server/pm/PackageSessionVerifier.java36
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java1
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java30
-rw-r--r--services/core/java/com/android/server/pm/ScanResult.java4
-rw-r--r--services/core/java/com/android/server/pm/Settings.java30
-rw-r--r--services/core/java/com/android/server/pm/SharedUidMigration.java99
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java19
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java28
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING6
-rw-r--r--services/core/java/com/android/server/pm/UserDataPreparer.java7
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java56
-rw-r--r--services/core/java/com/android/server/pm/VerificationParams.java9
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java7
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java16
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java12
-rw-r--r--services/core/java/com/android/server/policy/KeyCombinationManager.java24
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyInternal.java17
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java72
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java18
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java4
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java15
-rw-r--r--services/core/java/com/android/server/trust/TEST_MAPPING15
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java12
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java22
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java5
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java98
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java106
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java18
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java23
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java184
-rw-r--r--services/core/java/com/android/server/wm/DragState.java40
-rw-r--r--services/core/java/com/android/server/wm/InputConfigAdapter.java138
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java12
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java60
-rw-r--r--services/core/java/com/android/server/wm/InputWindowHandleWrapper.java65
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java7
-rw-r--r--services/core/java/com/android/server/wm/RefreshRatePolicy.java52
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java168
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java29
-rw-r--r--services/core/java/com/android/server/wm/Task.java26
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java15
-rw-r--r--services/core/java/com/android/server/wm/Transition.java4
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java60
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java46
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java239
-rw-r--r--services/java/com/android/server/SystemServer.java12
-rw-r--r--services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java84
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java20
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt22
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt5
-rw-r--r--services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java62
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java25
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java24
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java20
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java69
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java62
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java21
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java69
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java5
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java12
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt49
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java10
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java4
-rw-r--r--tests/TrustTests/TEST_MAPPING15
-rw-r--r--tools/aapt2/ResourceParser.cpp7
-rwxr-xr-xtools/aapt2/tools/finalize_res.py141
-rwxr-xr-xtools/finalize_res/finalize_res.py41
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java19
483 files changed, 14328 insertions, 10952 deletions
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index db5ba2fd031f..0f3ea0ca298f 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -33,13 +33,14 @@ gensrcs {
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
output_extension: "srcjar",
}
@@ -63,12 +64,13 @@ gensrcs {
" $(in)",
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
output_extension: "proto.h",
@@ -78,6 +80,7 @@ gensrcs {
java_library_host {
name: "platformprotos",
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
@@ -87,7 +90,7 @@ java_library_host {
"cmds/statsd/src/**/*.proto",
"core/proto/**/*.proto",
"libs/incident/proto/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
proto: {
include_dirs: [
@@ -117,12 +120,13 @@ java_library {
],
sdk_version: "9",
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/proto/android/os/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
// Protos have lots of MissingOverride and similar.
errorprone: {
@@ -139,12 +143,13 @@ java_library {
},
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/proto/android/os/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
exclude_srcs: [
"core/proto/android/privacy.proto",
@@ -176,11 +181,12 @@ cc_defaults {
],
srcs: [
+ ":framework-connectivity-protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
"core/proto/**/*.proto",
- ":service-permission-protos",
+ ":service-permission-streaming-proto-sources",
],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index f0b86df676e1..ec87591918bd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8,6 +8,9 @@ package android {
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
+ field public static final String ACCESS_ADSERVICES_ATTRIBUTION = "android.permission.ACCESS_ADSERVICES_ATTRIBUTION";
+ field public static final String ACCESS_ADSERVICES_CUSTOM_AUDIENCES = "android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCES";
+ field public static final String ACCESS_ADSERVICES_TOPICS = "android.permission.ACCESS_ADSERVICES_TOPICS";
field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS";
field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
@@ -17,7 +20,6 @@ package android {
field public static final String ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION";
field public static final String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
field public static final String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
- field public static final String ACCESS_SUPPLEMENTAL_APIS = "android.permission.ACCESS_SUPPLEMENTAL_APIS";
field public static final String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
field public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
@@ -979,8 +981,8 @@ package android {
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
field public static final int level = 16844032; // 0x1010500
- field public static final int lineBreakStyle = 16844365; // 0x101064d
- field public static final int lineBreakWordStyle = 16844366; // 0x101064e
+ field public static final int lineBreakStyle;
+ field public static final int lineBreakWordStyle;
field public static final int lineHeight = 16844159; // 0x101057f
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -2094,12 +2096,8 @@ package android {
field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
- field public static final int accessibilityActionShowSuggestions;
+ field public static final int accessibilityActionShowTextSuggestions;
field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044
- field public static final int accessibilityActionSwipeDown;
- field public static final int accessibilityActionSwipeLeft;
- field public static final int accessibilityActionSwipeRight;
- field public static final int accessibilityActionSwipeUp;
field public static final int accessibilitySystemActionBack = 16908363; // 0x102004b
field public static final int accessibilitySystemActionHome = 16908364; // 0x102004c
field public static final int accessibilitySystemActionLockScreen = 16908370; // 0x1020052
@@ -7439,7 +7437,7 @@ package android.app.admin {
method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName);
- method @NonNull public android.app.admin.PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig();
+ method @NonNull public java.util.List<android.app.admin.PreferentialNetworkServiceConfig> getPreferentialNetworkServiceConfigs();
method public int getRequiredPasswordComplexity();
method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
@@ -7584,7 +7582,7 @@ package android.app.admin {
method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean);
- method public void setPreferentialNetworkServiceConfig(@NonNull android.app.admin.PreferentialNetworkServiceConfig);
+ method public void setPreferentialNetworkServiceConfigs(@NonNull java.util.List<android.app.admin.PreferentialNetworkServiceConfig>);
method public void setPreferentialNetworkServiceEnabled(boolean);
method public void setProfileEnabled(@NonNull android.content.ComponentName);
method public void setProfileName(@NonNull android.content.ComponentName, String);
@@ -51931,12 +51929,8 @@ package android.view.accessibility {
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
- field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_SUGGESTIONS;
+ field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TEXT_SUGGESTIONS;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TOOLTIP;
- field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_DOWN;
- field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_LEFT;
- field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_RIGHT;
- field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_UP;
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> CREATOR;
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 5bc5bbc08074..241e5c8d97c4 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -20,10 +20,6 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
- public class ActivityOptions {
- method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
- }
-
public class AppOpsManager {
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
@@ -54,22 +50,6 @@ package android.app {
method public void onCanceled(@NonNull android.app.PendingIntent);
}
- public class PropertyInvalidatedCache<Query, Result> {
- ctor public PropertyInvalidatedCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
- method public final void disableForCurrentProcess();
- method public final void invalidateCache();
- method public static void invalidateCache(@NonNull String, @NonNull String);
- method @Nullable public final Result query(@NonNull Query);
- field public static final String MODULE_BLUETOOTH = "bluetooth";
- field public static final String MODULE_TELEPHONY = "telephony";
- }
-
- public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
- ctor public PropertyInvalidatedCache.QueryHandler();
- method @Nullable public abstract R apply(@NonNull Q);
- method public boolean shouldBypassCache(@NonNull Q);
- }
-
public class StatusBarManager {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
@@ -358,13 +338,29 @@ package android.os {
field public static final int DEVICE_INITIAL_SDK_INT;
}
+ public class IpcDataCache<Query, Result> {
+ ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
+ method public void disableForCurrentProcess();
+ method public static void disableForCurrentProcess(@NonNull String);
+ method public void invalidateCache();
+ method public static void invalidateCache(@NonNull String, @NonNull String);
+ method @Nullable public Result query(@NonNull Query);
+ field public static final String MODULE_BLUETOOTH = "bluetooth";
+ }
+
+ public abstract static class IpcDataCache.QueryHandler<Q, R> {
+ ctor public IpcDataCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public interface Parcelable {
method public default int getStability();
}
public class Process {
+ method public static final int getAppUidForSdkSandboxUid(int);
method public static final boolean isSdkSandboxUid(int);
- method public static final int sdkSandboxToAppUid(int);
method public static final int toSdkSandboxUid(int);
field public static final int NFC_UID = 1027; // 0x403
field public static final int VPN_UID = 1016; // 0x3f8
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 15666017f431..06f1ac1c0d3c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -234,6 +234,7 @@ package android {
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
+ field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE";
field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
@@ -1101,7 +1102,7 @@ package android.app.admin {
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull String[]);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]);
method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -1254,6 +1255,7 @@ package android.app.admin {
method @Nullable public java.util.Locale getLocale();
method @NonNull public String getOwnerName();
method @Nullable public String getTimeZone();
+ method public boolean isDemoDevice();
method public boolean isLeaveAllSystemAppsEnabled();
method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
@@ -1264,6 +1266,7 @@ package android.app.admin {
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDemoDevice(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
@@ -9027,6 +9030,7 @@ package android.net.wifi.nl80211 {
method public void enableVerboseLogging(boolean);
method @NonNull public int[] getChannelsMhzForBand(int);
method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
+ method public int getMaxNumScanSsids(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
method public void notifyCountryCodeChanged(@Nullable String);
@@ -11402,7 +11406,7 @@ package android.service.games {
public static interface GameSession.ScreenshotCallback {
method public void onFailure(int);
- method public void onSuccess(@NonNull android.graphics.Bitmap);
+ method public void onSuccess();
field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 58e59fd9430b..a22c4bcc3720 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -154,7 +154,6 @@ package android.app {
}
public class ActivityOptions {
- method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
method public boolean isEligibleForLegacyPermissionPrompt();
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
@@ -374,16 +373,17 @@ package android.app {
public class PropertyInvalidatedCache<Query, Result> {
ctor public PropertyInvalidatedCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
method @NonNull public static String createPropertyName(@NonNull String, @NonNull String);
- method public final void disableForCurrentProcess();
+ method public void disableForCurrentProcess();
+ method public static void disableForCurrentProcess(@NonNull String);
method public static void disableForTestMode();
method public final void disableInstance();
method public final void disableSystemWide();
method public final void forgetDisableLocal();
method public boolean getDisabledState();
- method public final void invalidateCache();
+ method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
method public final boolean isDisabled();
- method @Nullable public final Result query(@NonNull Query);
+ method @Nullable public Result query(@NonNull Query);
method public static void setTestMode(boolean);
method public void testPropertyName();
field public static final String MODULE_BLUETOOTH = "bluetooth";
@@ -1466,6 +1466,7 @@ package android.media {
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+ method @Nullable public static String getHalVersion();
method public static final int[] getPublicStreamTypes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
method public int getStreamMinVolumeInt(int);
@@ -1726,6 +1727,19 @@ package android.os {
method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException;
}
+ public class IpcDataCache<Query, Result> extends android.app.PropertyInvalidatedCache<Query,Result> {
+ ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
+ method public static void disableForCurrentProcess(@NonNull String);
+ method public static void invalidateCache(@NonNull String, @NonNull String);
+ field public static final String MODULE_BLUETOOTH = "bluetooth";
+ field public static final String MODULE_SYSTEM = "system_server";
+ field public static final String MODULE_TEST = "test";
+ }
+
+ public abstract static class IpcDataCache.QueryHandler<Q, R> extends android.app.PropertyInvalidatedCache.QueryHandler<Q,R> {
+ ctor public IpcDataCache.QueryHandler();
+ }
+
public final class MessageQueue {
method public int postSyncBarrier();
method public void removeSyncBarrier(int);
@@ -1777,9 +1791,9 @@ package android.os {
}
public class Process {
+ method public static final int getAppUidForSdkSandboxUid(int);
method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
method public static final boolean isSdkSandboxUid(int);
- method public static final int sdkSandboxToAppUid(int);
method public static final int toSdkSandboxUid(int);
field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
@@ -1787,6 +1801,7 @@ package android.os {
field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
field public static final int NFC_UID = 1027; // 0x403
field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
+ field public static final int SDK_SANDBOX_VIRTUAL_UID = 1090; // 0x442
}
public final class StrictMode {
@@ -2877,6 +2892,7 @@ package android.view {
method public static int getHoverTooltipHideTimeout();
method public static int getHoverTooltipShowTimeout();
method public static int getLongPressTooltipHideTimeout();
+ method public int getPreferKeepClearForFocusDelay();
}
public class ViewDebug {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 87ac6cb1fe4c..0178fa143445 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1421,17 +1421,9 @@ public class ActivityOptions extends ComponentOptions {
return mRemoteTransition;
}
- /**
- * Creates an ActivityOptions from the Bundle generated from {@link ActivityOptions#toBundle()}.
- * Returns an instance of ActivityOptions populated with options with known keys from the
- * provided Bundle, stripping out unknown entries.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- @TestApi
- @NonNull
- public static ActivityOptions fromBundle(@NonNull Bundle bOptions) {
- return new ActivityOptions(bOptions);
+ /** @hide */
+ public static ActivityOptions fromBundle(Bundle bOptions) {
+ return bOptions != null ? new ActivityOptions(bOptions) : null;
}
/** @hide */
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a82ecce2dc04..4fbe232556ed 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -116,7 +116,7 @@ interface INotificationManager
ParceledListSlice getNotificationChannelGroups(String pkg);
boolean onlyHasDefaultChannel(String pkg, int uid);
boolean areChannelsBypassingDnd();
- ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
+ ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid);
boolean isPackagePaused(String pkg);
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
boolean isPermissionFixed(String pkg, int userId);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 2f202d95e0e3..df7bf7b94700 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -18,7 +18,6 @@ package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Handler;
import android.os.Looper;
@@ -137,6 +136,26 @@ import java.util.concurrent.atomic.AtomicLong;
* With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
* for the first time; on subsequent queries, we return the already-known Birthday object.
*
+ * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
+ * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
+ * string is permitted. The third parameters is the name of the API being cached; this, too, can
+ * any value. The fourth is the name of the cache. The cache is usually named after th API.
+ * Some things you must know about the three strings:
+ * <list>
+ * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * Usually, the SELinux rules permit a process to write a system property (and therefore
+ * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}. This means that
+ * although the cache can be constructed with any module string, whatever string is chosen must be
+ * consistent with the SELinux configuration.
+ * <ul> The API name can be any string of alphanumeric characters. All caches with the same API
+ * are invalidated at the same time. If a server supports several caches and all are invalidated
+ * in common, then it is most efficient to assign the same API string to every cache.
+ * <ul> The cache name can be any string. In debug output, the name is used to distiguish between
+ * caches with the same API name. The cache name is also used when disabling caches in the
+ * current process. So, invalidation is based on the module+api but disabling (which is generally
+ * a once-per-process operation) is based on the cache name.
+ * </list>
+ *
* User birthdays do occasionally change, so we have to modify the server to invalidate this
* cache when necessary. That invalidation code looks like this:
*
@@ -192,25 +211,23 @@ import java.util.concurrent.atomic.AtomicLong;
* <pre>
* public class ActivityThread {
* ...
- * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
- * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
- * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * {@literal @}Override
- * protected Birthday recompute(Integer userId) {
- * return GetService("birthdayd").getUserBirthday(userId);
- * }
- * {@literal @}Override
- * protected boolean bypass(Integer userId) {
- * return userId == NEXT_BIRTHDAY;
- * }
- * };
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * public boolean shouldBypassQuery(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
* ...
* }
* </pre>
*
- * If the {@code bypass()} method returns true then the cache is not used for that
- * particular query. The {@code bypass()} method is not abstract and the default
+ * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
+ * particular query. The {@code shouldBypassQuery()} method is not abstract and the default
* implementation returns false.
*
* For security, there is a allowlist of processes that are allowed to invalidate a cache.
@@ -231,14 +248,12 @@ import java.util.concurrent.atomic.AtomicLong;
* @param <Result> The class holding cache entries; use a boxed primitive if possible
* @hide
*/
-@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public class PropertyInvalidatedCache<Query, Result> {
/**
* This is a configuration class that customizes a cache instance.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static abstract class QueryHandler<Q,R> {
/**
@@ -285,7 +300,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* The module used for bluetooth caches.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static final String MODULE_BLUETOOTH = "bluetooth";
@@ -533,7 +547,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* @param computer The code to compute values that are not in the cache.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
@@ -792,7 +805,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* TODO(216112648) Remove this in favor of disableForCurrentProcess().
* @hide
*/
- public final void disableLocal() {
+ public void disableLocal() {
disableForCurrentProcess();
}
@@ -802,12 +815,17 @@ public class PropertyInvalidatedCache<Query, Result> {
* property.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final void disableForCurrentProcess() {
+ public void disableForCurrentProcess() {
disableLocal(mCacheName);
}
+ /** @hide */
+ @TestApi
+ public static void disableForCurrentProcess(@NonNull String cacheName) {
+ disableLocal(cacheName);
+ }
+
/**
* Return whether a cache instance is disabled.
* @hide
@@ -821,9 +839,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* Get a value from the cache or recompute it.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final @Nullable Result query(@NonNull Query query) {
+ public @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
if (bypass(query)) {
@@ -964,9 +981,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* PropertyInvalidatedCache is keyed on a particular property value.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public final void invalidateCache() {
+ public void invalidateCache() {
invalidateCache(mPropertyName);
}
@@ -974,7 +990,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* Invalidate caches in all processes that are keyed for the module and api.
* @hide
*/
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static void invalidateCache(@NonNull String module, @NonNull String api) {
invalidateCache(createPropertyName(module, api));
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b436f6e7374f..4c7b91056ca1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest.permission;
@@ -1413,6 +1415,9 @@ public class DevicePolicyManager {
* admin app when performing the admin-integrated provisioning flow as a result of the
* {@link #ACTION_GET_PROVISIONING_MODE} activity.
*
+ * <p>This extra may also be provided to the admin app via an intent extra for {@link
+ * #ACTION_GET_PROVISIONING_MODE}.
+ *
* @see #ACTION_GET_PROVISIONING_MODE
*/
public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT =
@@ -3062,6 +3067,8 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li>
* <li>{@link #EXTRA_PROVISIONING_IMEI}</li>
* <li>{@link #EXTRA_PROVISIONING_SERIAL_NUMBER}</li>
+ * <li>{@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES}</li>
+ * <li>{@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT}</li>
* </ul>
*
* <p>The target activity should return one of the following values in
@@ -3085,8 +3092,22 @@ public class DevicePolicyManager {
* activity, along with the values of the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra
* that are already supplied to this activity.
*
- * @see #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION
- * @see #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED
+ * <p>Other extras the target activity may include in the intent result:
+ * <ul>
+ * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}</li>
+ * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}</li>
+ * <li>{@link #EXTRA_PROVISIONING_KEEP_SCREEN_ON}</li>
+ * <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION} for work profile
+ * provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED} for work profile
+ * provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT} for fully-managed
+ * device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LOCALE} for fully-managed device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} for fully-managed device provisioning</li>
+ * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE} for fully-managed device provisioning</li>
+ * </ul>
+ *
* @see #ACTION_ADMIN_POLICY_COMPLIANCE
*/
public static final String ACTION_GET_PROVISIONING_MODE =
@@ -11012,7 +11033,7 @@ public class DevicePolicyManager {
}
/**
- * Sets whether preferential network service is enabled on the work profile.
+ * Sets whether preferential network service is enabled.
* For example, an organization can have a deal/agreement with a carrier that all of
* the work data from its employees’ devices will be sent via a network service dedicated
* for enterprise use.
@@ -11020,75 +11041,72 @@ public class DevicePolicyManager {
* An example of a supported preferential network service is the Enterprise
* slice on 5G networks.
*
- * By default, preferential network service is disabled on the work profile on supported
- * carriers and devices. Admins can explicitly enable it with this API.
- * On fully-managed devices this method is unsupported because all traffic is considered
- * work traffic.
+ * By default, preferential network service is disabled on the work profile and
+ * fully managed devices, on supported carriers and devices.
+ * Admins can explicitly enable it with this API.
*
* <p> This method enables preferential network service with a default configuration.
- * To fine-tune the configuration, use {@link #setPreferentialNetworkServiceConfig) instead.
+ * To fine-tune the configuration, use {@link #setPreferentialNetworkServiceConfigs) instead.
+ * <p> Before Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * this method can be called by the profile owner of a managed profile.
+ * <p> Starting from Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * This method can be called by the profile owner of a managed profile
+ * or device owner.
*
- * <p>This method can only be called by the profile owner of a managed profile.
* @param enabled whether preferential network service should be enabled.
- * @throws SecurityException if the caller is not the profile owner.
+ * @throws SecurityException if the caller is not the profile owner or device owner.
**/
public void setPreferentialNetworkServiceEnabled(boolean enabled) {
throwIfParentInstance("setPreferentialNetworkServiceEnabled");
- if (mService == null) {
- return;
- }
-
- try {
- mService.setPreferentialNetworkServiceEnabled(enabled);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ PreferentialNetworkServiceConfig.Builder configBuilder =
+ new PreferentialNetworkServiceConfig.Builder();
+ configBuilder.setEnabled(enabled);
+ if (enabled) {
+ configBuilder.setNetworkId(NET_ENTERPRISE_ID_1);
}
+ setPreferentialNetworkServiceConfigs(List.of(configBuilder.build()));
}
/**
* Indicates whether preferential network service is enabled.
*
- * <p>This method can be called by the profile owner of a managed profile.
+ * <p> Before Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * This method can be called by the profile owner of a managed profile.
+ * <p> Starting from Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * This method can be called by the profile owner of a managed profile
+ * or device owner.
*
* @return whether preferential network service is enabled.
- * @throws SecurityException if the caller is not the profile owner.
+ * @throws SecurityException if the caller is not the profile owner or device owner.
*/
public boolean isPreferentialNetworkServiceEnabled() {
throwIfParentInstance("isPreferentialNetworkServiceEnabled");
- if (mService == null) {
- return false;
- }
- try {
- return mService.isPreferentialNetworkServiceEnabled(myUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getPreferentialNetworkServiceConfigs().stream().anyMatch(c -> c.isEnabled());
}
/**
- * Sets preferential network configuration on the work profile.
+ * Sets preferential network configurations.
* {@see PreferentialNetworkServiceConfig}
*
* An example of a supported preferential network service is the Enterprise
* slice on 5G networks.
*
- * By default, preferential network service is disabled on the work profile on supported
- * carriers and devices. Admins can explicitly enable it with this API.
- * On fully-managed devices this method is unsupported because all traffic is considered
- * work traffic.
+ * By default, preferential network service is disabled on the work profile and fully managed
+ * devices, on supported carriers and devices. Admins can explicitly enable it with this API.
+ * If admin wants to have multiple enterprise slices,
+ * it can be configured by passing list of {@link PreferentialNetworkServiceConfig} objects.
*
- * <p>This method can only be called by the profile owner of a managed profile.
- * @param preferentialNetworkServiceConfig preferential network configuration.
- * @throws SecurityException if the caller is not the profile owner.
+ * @param preferentialNetworkServiceConfigs list of preferential network configurations.
+ * @throws SecurityException if the caller is not the profile owner or device owner.
**/
- public void setPreferentialNetworkServiceConfig(
- @NonNull PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
- throwIfParentInstance("setPreferentialNetworkServiceConfig");
+ public void setPreferentialNetworkServiceConfigs(
+ @NonNull List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
+ throwIfParentInstance("setPreferentialNetworkServiceConfigs");
if (mService == null) {
return;
}
try {
- mService.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfig);
+ mService.setPreferentialNetworkServiceConfigs(preferentialNetworkServiceConfigs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -11098,18 +11116,16 @@ public class DevicePolicyManager {
* Get preferential network configuration
* {@see PreferentialNetworkServiceConfig}
*
- * <p>This method can be called by the profile owner of a managed profile.
- *
* @return preferential network configuration.
- * @throws SecurityException if the caller is not the profile owner.
+ * @throws SecurityException if the caller is not the profile owner or device owner.
*/
- public @NonNull PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() {
- throwIfParentInstance("getPreferentialNetworkServiceConfig");
+ public @NonNull List<PreferentialNetworkServiceConfig> getPreferentialNetworkServiceConfigs() {
+ throwIfParentInstance("getPreferentialNetworkServiceConfigs");
if (mService == null) {
- return PreferentialNetworkServiceConfig.DEFAULT;
+ return List.of(PreferentialNetworkServiceConfig.DEFAULT);
}
try {
- return mService.getPreferentialNetworkServiceConfig();
+ return mService.getPreferentialNetworkServiceConfigs();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -13593,13 +13609,18 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to add an override APN.
+ * Called by device owner or profile owner to add an override APN.
*
* <p>This method may returns {@code -1} if {@code apnSetting} conflicts with an existing
* override APN. Update the existing conflicted APN with
* {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry.
* <p>Two override APNs are considered to conflict when all the following APIs return
* the same values on both override APNs:
+ * <p> Before Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Only device owners can add APNs.
+ * <p> Starting from Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Device and profile owners can add enterprise APNs
+ * ({@link ApnSetting#TYPE_ENTERPRISE}), while only device owners can add other type of APNs.
* <ul>
* <li>{@link ApnSetting#getOperatorNumeric()}</li>
* <li>{@link ApnSetting#getApnName()}</li>
@@ -13618,7 +13639,8 @@ public class DevicePolicyManager {
* @param apnSetting the override APN to insert
* @return The {@code id} of inserted override APN. Or {@code -1} when failed to insert into
* the database.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException If request is for enterprise APN {@code admin} is either device
+ * owner or profile owner and in all other types of APN if {@code admin} is not a device owner.
*
* @see #setOverrideApnsEnabled(ComponentName, boolean)
*/
@@ -13635,20 +13657,26 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to update an override APN.
+ * Called by device owner or profile owner to update an override APN.
*
* <p>This method may returns {@code false} if there is no override APN with the given
* {@code apnId}.
* <p>This method may also returns {@code false} if {@code apnSetting} conflicts with an
* existing override APN. Update the existing conflicted APN instead.
* <p>See {@link #addOverrideApn} for the definition of conflict.
+ * <p> Before Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Only device owners can update APNs.
+ * <p> Starting from Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Device and profile owners can update enterprise APNs
+ * ({@link ApnSetting#TYPE_ENTERPRISE}), while only device owners can update other type of APNs.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnId the {@code id} of the override APN to update
* @param apnSetting the override APN to update
* @return {@code true} if the required override APN is successfully updated,
* {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException If request is for enterprise APN {@code admin} is either device
+ * owner or profile owner and in all other types of APN if {@code admin} is not a device owner.
*
* @see #setOverrideApnsEnabled(ComponentName, boolean)
*/
@@ -13666,16 +13694,22 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to remove an override APN.
+ * Called by device owner or profile owner to remove an override APN.
*
* <p>This method may returns {@code false} if there is no override APN with the given
* {@code apnId}.
+ * <p> Before Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Only device owners can remove APNs.
+ * <p> Starting from Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}:
+ * Device and profile owners can remove enterprise APNs
+ * ({@link ApnSetting#TYPE_ENTERPRISE}), while only device owners can remove other type of APNs.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @param apnId the {@code id} of the override APN to remove
* @return {@code true} if the required override APN is successfully removed, {@code false}
* otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException If request is for enterprise APN {@code admin} is either device
+ * owner or profile owner and in all other types of APN if {@code admin} is not a device owner.
*
* @see #setOverrideApnsEnabled(ComponentName, boolean)
*/
@@ -14776,17 +14810,20 @@ public class DevicePolicyManager {
* <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
* before calling this method.
*
+ * <p>Holders of {@link android.Manifest.permission#PROVISION_DEMO_DEVICE} can call this API
+ * only if {@link FullyManagedDeviceProvisioningParams#isDemoDevice()} is {@code true}.</p>
+ *
* @param provisioningParams Params required to provision a fully managed device,
* see {@link FullyManagedDeviceProvisioningParams}.
*
- * @throws SecurityException if the caller does not hold
- * {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}.
* @throws ProvisioningException if an error occurred during provisioning.
*
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+ android.Manifest.permission.PROVISION_DEMO_DEVICE})
public void provisionFullyManagedDevice(
@NonNull FullyManagedDeviceProvisioningParams provisioningParams)
throws ProvisioningException {
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 1f7ae4ad35de..49992452df86 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -26,6 +26,7 @@ import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.provider.Settings;
import android.stats.devicepolicy.DevicePolicyEnums;
import java.util.Locale;
@@ -44,6 +45,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
"CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS";
private static final String TIME_ZONE_PROVIDED_PARAM = "TIME_ZONE_PROVIDED";
private static final String LOCALE_PROVIDED_PARAM = "LOCALE_PROVIDED";
+ private static final String DEMO_DEVICE = "DEMO_DEVICE";
@NonNull private final ComponentName mDeviceAdminComponentName;
@NonNull private final String mOwnerName;
@@ -54,6 +56,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
@Nullable private final Locale mLocale;
private final boolean mDeviceOwnerCanGrantSensorsPermissions;
@NonNull private final PersistableBundle mAdminExtras;
+ private final boolean mDemoDevice;
+
private FullyManagedDeviceProvisioningParams(
@NonNull ComponentName deviceAdminComponentName,
@@ -63,7 +67,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
long localTime,
@Nullable @SuppressLint("UseIcu") Locale locale,
boolean deviceOwnerCanGrantSensorsPermissions,
- @NonNull PersistableBundle adminExtras) {
+ @NonNull PersistableBundle adminExtras,
+ boolean demoDevice) {
this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
@@ -73,6 +78,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
this.mDeviceOwnerCanGrantSensorsPermissions =
deviceOwnerCanGrantSensorsPermissions;
this.mAdminExtras = adminExtras;
+ this.mDemoDevice = demoDevice;
}
private FullyManagedDeviceProvisioningParams(
@@ -83,7 +89,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
long localTime,
@Nullable String localeStr,
boolean deviceOwnerCanGrantSensorsPermissions,
- @Nullable PersistableBundle adminExtras) {
+ @Nullable PersistableBundle adminExtras,
+ boolean demoDevice) {
this(deviceAdminComponentName,
ownerName,
leaveAllSystemAppsEnabled,
@@ -91,7 +98,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
localTime,
getLocale(localeStr),
deviceOwnerCanGrantSensorsPermissions,
- adminExtras);
+ adminExtras,
+ demoDevice);
}
@Nullable
@@ -166,6 +174,14 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
}
/**
+ * @return true if this device is being setup as a retail demo device, see
+ * {@link Settings.Global#DEVICE_DEMO_MODE}.
+ */
+ public boolean isDemoDevice() {
+ return mDemoDevice;
+ }
+
+ /**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
*
* @hide
@@ -178,6 +194,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
mDeviceOwnerCanGrantSensorsPermissions);
logParam(callerPackage, TIME_ZONE_PROVIDED_PARAM, /* value= */ mTimeZone != null);
logParam(callerPackage, LOCALE_PROVIDED_PARAM, /* value= */ mLocale != null);
+ logParam(callerPackage, DEMO_DEVICE, mDemoDevice);
}
private void logParam(String callerPackage, String param, boolean value) {
@@ -204,6 +221,9 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
// Default to allowing control over sensor permission grants.
boolean mDeviceOwnerCanGrantSensorsPermissions = true;
@NonNull private PersistableBundle mAdminExtras;
+ // Default is normal user devices
+ boolean mDemoDevice = false;
+
/**
* Initialize a new {@link Builder} to construct a
@@ -289,6 +309,16 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
}
/**
+ * Marks the device as a demo device, see {@link Settings.Global#DEVICE_DEMO_MODE}. The
+ * default value if unset is {@code false}.
+ */
+ @NonNull
+ public Builder setDemoDevice(boolean demoDevice) {
+ this.mDemoDevice = demoDevice;
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}
*
* @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -303,7 +333,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
mLocalTime,
mLocale,
mDeviceOwnerCanGrantSensorsPermissions,
- mAdminExtras);
+ mAdminExtras,
+ mDemoDevice);
}
}
@@ -327,6 +358,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
+ ", mDeviceOwnerCanGrantSensorsPermissions="
+ mDeviceOwnerCanGrantSensorsPermissions
+ ", mAdminExtras=" + mAdminExtras
+ + ", mDemoDevice=" + mDemoDevice
+ '}';
}
@@ -340,6 +372,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
dest.writePersistableBundle(mAdminExtras);
+ dest.writeBoolean(mDemoDevice);
}
@NonNull
@@ -355,6 +388,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
String locale = in.readString();
boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
PersistableBundle adminExtras = in.readPersistableBundle();
+ boolean demoDevice = in.readBoolean();
return new FullyManagedDeviceProvisioningParams(
componentName,
@@ -364,7 +398,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
localtime,
locale,
deviceOwnerCanGrantSensorsPermissions,
- adminExtras);
+ adminExtras,
+ demoDevice);
}
@Override
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9d28ddefda7b..77db14654592 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -285,12 +285,9 @@ interface IDevicePolicyManager {
void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
boolean isSecondaryLockscreenEnabled(in UserHandle userHandle);
- void setPreferentialNetworkServiceEnabled(in boolean enabled);
- boolean isPreferentialNetworkServiceEnabled(int userHandle);
-
- void setPreferentialNetworkServiceConfig(
- in PreferentialNetworkServiceConfig preferentialNetworkServiceConfig);
- PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig();
+ void setPreferentialNetworkServiceConfigs(
+ in List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs);
+ List<PreferentialNetworkServiceConfig> getPreferentialNetworkServiceConfigs();
void setLockTaskPackages(in ComponentName who, in String[] packages);
String[] getLockTaskPackages(in ComponentName who);
diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
index 2849139c606b..54170a2a187f 100644
--- a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
+++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
@@ -28,7 +28,7 @@ import java.util.Objects;
/**
* Network configuration to be set for the user profile
- * {@see DevicePolicyManager#setPreferentialNetworkServiceConfig}.
+ * {@see DevicePolicyManager#setPreferentialNetworkServiceConfigs}.
*/
public final class PreferentialNetworkServiceConfig implements Parcelable {
final boolean mIsEnabled;
@@ -147,8 +147,6 @@ public final class PreferentialNetworkServiceConfig implements Parcelable {
/**
* @return preference enterprise identifier.
- * valid values starts from
- * {@link #PREFERENTIAL_NETWORK_ID_1} to {@link #PREFERENTIAL_NETWORK_ID_5}.
* preference identifier is applicable only if preference network service is enabled
*
*/
@@ -286,8 +284,6 @@ public final class PreferentialNetworkServiceConfig implements Parcelable {
/**
* Set the preferential network identifier.
- * Valid values starts from {@link #PREFERENTIAL_NETWORK_ID_1} to
- * {@link #PREFERENTIAL_NETWORK_ID_5}.
* preference identifier is applicable only if preferential network service is enabled.
* @param preferenceId preference Id
* @return The builder to facilitate chaining.
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 6a6d76d20259..469a9bfe59ef 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -64,11 +64,11 @@ public abstract class ActivityTransactionItem extends ClientTransactionItem {
final ActivityClientRecord r = client.getActivityClient(token);
if (r == null) {
throw new IllegalArgumentException("Activity client record must not be null to execute "
- + "transaction item");
+ + "transaction item: " + this);
}
if (client.getActivity(token) == null) {
throw new IllegalArgumentException("Activity must not be null to execute "
- + "transaction item");
+ + "transaction item: " + this);
}
return r;
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index abf1058f45a2..d7e09519bfb7 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -179,7 +179,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readPersistableBundle(getClass().getClassLoader()),
in.createTypedArrayList(ResultInfo.CREATOR),
in.createTypedArrayList(ReferrerIntent.CREATOR),
- readActivityOptions(in), in.readBoolean(),
+ ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -187,11 +187,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readBoolean());
}
- private static ActivityOptions readActivityOptions(Parcel in) {
- Bundle bundle = in.readBundle();
- return bundle != null ? ActivityOptions.fromBundle(bundle) : null;
- }
-
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
new Creator<LaunchActivityItem>() {
public LaunchActivityItem createFromParcel(Parcel in) {
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index f267060d1be6..15f65f6d9d26 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Trace;
@@ -84,8 +83,7 @@ public class StartActivityItem extends ActivityLifecycleItem {
/** Read from Parcel. */
private StartActivityItem(Parcel in) {
- Bundle bundle = in.readBundle();
- mActivityOptions = bundle != null ? ActivityOptions.fromBundle(bundle) : null;
+ mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
}
public static final @NonNull Creator<StartActivityItem> CREATOR =
diff --git a/core/java/android/app/trust/TEST_MAPPING b/core/java/android/app/trust/TEST_MAPPING
new file mode 100644
index 000000000000..b9c46bfbba8c
--- /dev/null
+++ b/core/java/android/app/trust/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "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/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 673127e9f808..2961b5505794 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2084,7 +2084,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
splitNames = source.createString8Array();
splitSourceDirs = source.createString8Array();
splitPublicSourceDirs = source.createString8Array();
- splitDependencies = source.readSparseArray(null);
+ splitDependencies = source.readSparseArray(null, int[].class);
nativeLibraryDir = source.readString8();
secondaryNativeLibraryDir = source.readString8();
nativeLibraryRootDir = source.readString8();
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 18e205f2e79e..8d6c8e8d9f62 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -57,4 +57,5 @@ interface IPackageInstallerSession {
int getParentSessionId();
boolean isStaged();
+ int getInstallFlags();
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e9e6cd3a54c5..0f236dfe1a8b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -497,21 +497,10 @@ interface IPackageManager {
void enterSafeMode();
@UnsupportedAppUsage
boolean isSafeMode();
- void systemReady();
@UnsupportedAppUsage
boolean hasSystemUidErrors();
/**
- * Ask the package manager to fstrim the disk if needed.
- */
- void performFstrimIfNeeded();
-
- /**
- * Ask the package manager to update packages if needed.
- */
- void updatePackagesIfNeeded();
-
- /**
* Notify the package manager that a package is going to be used and why.
*
* See PackageManager.NOTIFY_PACKAGE_USE_* for reasons.
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 67a2dc84728d..450e09a307bf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1586,6 +1586,18 @@ public class PackageInstaller {
}
/**
+ * @return Session's {@link SessionParams#installFlags}.
+ * @hide
+ */
+ public int getInstallFlags() {
+ try {
+ return mSession.getInstallFlags();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return the session ID of the multi-package session that this belongs to or
* {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session.
*/
@@ -2421,15 +2433,6 @@ public class PackageInstaller {
/** {@hide} */
private static final int[] NO_SESSIONS = {};
- /** @hide */
- @IntDef(prefix = { "SESSION_" }, value = {
- SESSION_NO_ERROR,
- SESSION_VERIFICATION_FAILED,
- SESSION_ACTIVATION_FAILED,
- SESSION_UNKNOWN_ERROR,
- SESSION_CONFLICT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface SessionErrorCode {}
/**
* @deprecated use {@link #SESSION_NO_ERROR}.
*/
@@ -3113,7 +3116,7 @@ public class PackageInstaller {
* If something went wrong with a staged session, clients can check this error code to
* understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
*/
- public @SessionErrorCode int getStagedSessionErrorCode() {
+ public int getStagedSessionErrorCode() {
checkSessionIsStaged();
return mSessionErrorCode;
}
@@ -3128,7 +3131,7 @@ public class PackageInstaller {
}
/** {@hide} */
- public void setSessionErrorCode(@SessionErrorCode int errorCode, String errorMessage) {
+ public void setSessionErrorCode(int errorCode, String errorMessage) {
mSessionErrorCode = errorCode;
mSessionErrorMessage = errorMessage;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f4bc1616da2b..78dddb577c36 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -121,6 +121,9 @@ public abstract class PackageManager {
/** {@hide} */
public static final boolean APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = true;
+ /** {@hide} */
+ public static final boolean ENABLE_SHARED_UID_MIGRATION = true;
+
/**
* This exception is thrown when a given package, application, or component
* name cannot be found.
@@ -2202,6 +2205,14 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FAILED_BAD_PERMISSION_GROUP = -127;
+ /**
+ * Installation failed return code: an error occurred during the activation phase of this
+ * session.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ACTIVATION_FAILED = -128;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e914432630f7..4d4a57db84be 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1944,19 +1944,26 @@ public class PackageParser {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
- String str = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
- if (str != null && str.length() > 0) {
- String nameError = validateName(str, true, true);
- if (nameError != null && !"android".equals(pkg.packageName)) {
- outError[0] = "<manifest> specifies bad sharedUserId name \""
- + str + "\": " + nameError;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
- return null;
+ int maxSdkVersion = 0;
+ if (PackageManager.ENABLE_SHARED_UID_MIGRATION) {
+ maxSdkVersion = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifest_sharedUserMaxSdkVersion, 0);
+ }
+ if (maxSdkVersion == 0 || maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT) {
+ String str = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
+ if (str != null && str.length() > 0) {
+ String nameError = validateName(str, true, true);
+ if (nameError != null && !"android".equals(pkg.packageName)) {
+ outError[0] = "<manifest> specifies bad sharedUserId name \""
+ + str + "\": " + nameError;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
+ return null;
+ }
+ pkg.mSharedUserId = str.intern();
+ pkg.mSharedUserLabel = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
- pkg.mSharedUserId = str.intern();
- pkg.mSharedUserLabel = sa.getResourceId(
- com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
pkg.installLocation = sa.getInteger(
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index f34e2bf5ddc2..698cc76f6325 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -59,6 +59,7 @@ public class CameraSessionStats implements Parcelable {
private long mRequestCount;
private long mResultErrorCount;
private boolean mDeviceError;
+ private float mMaxPreviewFps;
private ArrayList<CameraStreamStats> mStreamStats;
public CameraSessionStats() {
@@ -67,6 +68,7 @@ public class CameraSessionStats implements Parcelable {
mApiLevel = -1;
mIsNdk = false;
mLatencyMs = -1;
+ mMaxPreviewFps = 0;
mSessionType = -1;
mInternalReconfigure = -1;
mRequestCount = 0;
@@ -77,7 +79,7 @@ public class CameraSessionStats implements Parcelable {
public CameraSessionStats(String cameraId, int facing, int newCameraState,
String clientName, int apiLevel, boolean isNdk, int creationDuration,
- int sessionType, int internalReconfigure) {
+ float maxPreviewFps, int sessionType, int internalReconfigure) {
mCameraId = cameraId;
mFacing = facing;
mNewCameraState = newCameraState;
@@ -85,6 +87,7 @@ public class CameraSessionStats implements Parcelable {
mApiLevel = apiLevel;
mIsNdk = isNdk;
mLatencyMs = creationDuration;
+ mMaxPreviewFps = maxPreviewFps;
mSessionType = sessionType;
mInternalReconfigure = internalReconfigure;
mStreamStats = new ArrayList<CameraStreamStats>();
@@ -121,6 +124,7 @@ public class CameraSessionStats implements Parcelable {
dest.writeInt(mApiLevel);
dest.writeBoolean(mIsNdk);
dest.writeInt(mLatencyMs);
+ dest.writeFloat(mMaxPreviewFps);
dest.writeInt(mSessionType);
dest.writeInt(mInternalReconfigure);
dest.writeLong(mRequestCount);
@@ -137,6 +141,7 @@ public class CameraSessionStats implements Parcelable {
mApiLevel = in.readInt();
mIsNdk = in.readBoolean();
mLatencyMs = in.readInt();
+ mMaxPreviewFps = in.readFloat();
mSessionType = in.readInt();
mInternalReconfigure = in.readInt();
mRequestCount = in.readLong();
@@ -176,6 +181,10 @@ public class CameraSessionStats implements Parcelable {
return mLatencyMs;
}
+ public float getMaxPreviewFps() {
+ return mMaxPreviewFps;
+ }
+
public int getSessionType() {
return mSessionType;
}
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index 823d454ee16b..7b24cc4836d0 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -15,8 +15,8 @@
*/
package android.hardware;
-import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -37,6 +37,7 @@ public class CameraStreamStats implements Parcelable {
private int mWidth;
private int mHeight;
private int mFormat;
+ private float mMaxPreviewFps;
private int mDataSpace;
private long mUsage;
private long mRequestCount;
@@ -56,6 +57,7 @@ public class CameraStreamStats implements Parcelable {
mWidth = 0;
mHeight = 0;
mFormat = 0;
+ mMaxPreviewFps = 0;
mDataSpace = 0;
mUsage = 0;
mRequestCount = 0;
@@ -68,13 +70,14 @@ public class CameraStreamStats implements Parcelable {
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
- public CameraStreamStats(int width, int height, int format,
+ public CameraStreamStats(int width, int height, int format, float maxPreviewFps,
int dataSpace, long usage, long requestCount, long errorCount,
int startLatencyMs, int maxHalBuffers, int maxAppBuffers, long dynamicRangeProfile,
int streamUseCase) {
mWidth = width;
mHeight = height;
mFormat = format;
+ mMaxPreviewFps = maxPreviewFps;
mDataSpace = dataSpace;
mUsage = usage;
mRequestCount = requestCount;
@@ -120,6 +123,7 @@ public class CameraStreamStats implements Parcelable {
dest.writeInt(mWidth);
dest.writeInt(mHeight);
dest.writeInt(mFormat);
+ dest.writeFloat(mMaxPreviewFps);
dest.writeInt(mDataSpace);
dest.writeLong(mUsage);
dest.writeLong(mRequestCount);
@@ -138,6 +142,7 @@ public class CameraStreamStats implements Parcelable {
mWidth = in.readInt();
mHeight = in.readInt();
mFormat = in.readInt();
+ mMaxPreviewFps = in.readFloat();
mDataSpace = in.readInt();
mUsage = in.readLong();
mRequestCount = in.readLong();
@@ -164,6 +169,10 @@ public class CameraStreamStats implements Parcelable {
return mFormat;
}
+ public float getMaxPreviewFps() {
+ return mMaxPreviewFps;
+ }
+
public int getDataSpace() {
return mDataSpace;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index abdc64c56ead..d8ebb628452a 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -298,4 +298,33 @@ public interface BiometricFingerprintConstants {
* @hide
*/
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+
+ /**
+ * Whether the FingerprintAcquired message is a signal to turn off HBM
+ */
+ static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) {
+ switch (acquiredInfo) {
+ case FINGERPRINT_ACQUIRED_START:
+ // Authentication just began
+ return false;
+ case FINGERPRINT_ACQUIRED_GOOD:
+ // Good image captured. Turn off HBM. Success/Reject comes after, which is when
+ // hideUdfpsOverlay will be called.
+ return true;
+ case FINGERPRINT_ACQUIRED_PARTIAL:
+ case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+ case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+ case FINGERPRINT_ACQUIRED_TOO_SLOW:
+ case FINGERPRINT_ACQUIRED_TOO_FAST:
+ case FINGERPRINT_ACQUIRED_IMMOBILE:
+ case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
+ case FINGERPRINT_ACQUIRED_VENDOR:
+ // Bad image captured. Turn off HBM. Matcher will not run, so there's no need to
+ // keep HBM on.
+ return true;
+ case FINGERPRINT_ACQUIRED_UNKNOWN:
+ default:
+ return false;
+ }
+ }
}
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index 648edda62171..3cca1b38e5e2 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -28,9 +28,10 @@ oneway interface IUdfpsOverlayController {
// Hides the overlay.
void hideUdfpsOverlay(int sensorId);
- // Good image captured. Turn off HBM. Success/Reject comes after, which is when hideUdfpsOverlay
- // will be called.
- void onAcquiredGood(int sensorId);
+ // Check acquiredInfo for the acquired type (BiometricFingerprintConstants#FingerprintAcquired).
+ // Check BiometricFingerprintConstants#shouldTurnOffHbm for whether the acquiredInfo
+ // should turn off HBM.
+ void onAcquired(int sensorId, int acquiredInfo);
// Notifies of enrollment progress changes.
void onEnrollmentProgress(int sensorId, int remaining);
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index af57f793bf73..02302a20fe38 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -40,6 +40,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -70,7 +71,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
- private static final int DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED = 35;
+ private static final int DO_ON_NAV_BUTTON_FLAGS_CHANGED = 35;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
private static final int DO_SHOW_SOFT_INPUT = 60;
@@ -176,7 +177,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
try {
inputMethod.initializeInternal((IBinder) args.arg1,
(IInputMethodPrivilegedOperations) args.arg2, msg.arg1,
- (boolean) args.arg3, msg.arg2 != 0);
+ (boolean) args.arg3, msg.arg2);
} finally {
args.recycle();
}
@@ -196,22 +197,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
- final boolean shouldShowImeSwitcherWhenImeIsShown = args.argi6 != 0;
+ @InputMethodNavButtonFlags
+ final int navButtonFlags = args.argi6;
final InputConnection ic = inputContext != null
? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
args.recycle();
return;
}
- case DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED: {
- final boolean shouldShowImeSwitcherWhenImeIsShown = msg.arg1 != 0;
- inputMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShown);
+ case DO_ON_NAV_BUTTON_FLAGS_CHANGED:
+ inputMethod.onNavButtonFlagsChanged(msg.arg1);
return;
- }
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
@@ -301,10 +300,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
@Override
public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @InputMethodNavButtonFlags int navButtonFlags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_INITIALIZE_INTERNAL,
- configChanges, shouldShowImeSwitcherWhenImeIsShown ? 1 : 0, token, privOps,
- stylusHwSupported));
+ configChanges, navButtonFlags, token, privOps, stylusHwSupported));
}
@BinderThread
@@ -344,23 +342,21 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
- EditorInfo attribute, boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ EditorInfo attribute, boolean restarting,
+ @InputMethodNavButtonFlags int navButtonFlags) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, restarting ? 1 : 0,
- shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
}
@BinderThread
@Override
- public void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageI(
- DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED,
- shouldShowImeSwitcherWhenImeIsShown ? 1 : 0));
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageI(DO_ON_NAV_BUTTON_FLAGS_CHANGED, navButtonFlags));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index fbc0732affe1..656aea18fe50 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -140,6 +140,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -347,7 +348,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@AnyThread
public static boolean canImeRenderGesturalNavButtons() {
- return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false);
+ return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
}
/**
@@ -660,7 +661,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags) {
if (mDestroyed) {
Log.i(TAG, "The InputMethodService has already onDestroyed()."
+ "Ignore the initialization.");
@@ -673,8 +674,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (stylusHwSupported) {
mInkWindow = new InkWindow(mWindow.getContext());
}
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -784,10 +784,9 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
mPrivOps.reportStartInputAsync(startInputToken);
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -801,10 +800,8 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
- mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown(
- shouldShowImeSwitcherWhenImeIsShown);
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
}
/**
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 19fa01de4a5f..0f9075b498ae 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,7 +16,6 @@
package android.inputmethodservice;
-import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import android.animation.ValueAnimator;
@@ -24,18 +23,12 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.inputmethodservice.navigationbar.NavigationBarFrame;
import android.inputmethodservice.navigationbar.NavigationBarView;
-import android.os.PatternMatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -49,6 +42,8 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
+
import java.util.Objects;
/**
@@ -77,8 +72,7 @@ final class NavigationBarController {
default void onDestroy() {
}
- default void setShouldShowImeSwitcherWhenImeIsShown(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
}
default String toDebugString() {
@@ -117,8 +111,8 @@ final class NavigationBarController {
mImpl.onDestroy();
}
- void setShouldShowImeSwitcherWhenImeIsShown(boolean shouldShowImeSwitcherWhenImeIsShown) {
- mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown);
+ void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
+ mImpl.onNavButtonFlagsChanged(navButtonFlags);
}
String toDebugString() {
@@ -144,9 +138,6 @@ final class NavigationBarController {
@Nullable
Insets mLastInsets;
- @Nullable
- private BroadcastReceiver mSystemOverlayChangedReceiver;
-
private boolean mShouldShowImeSwitcherWhenImeIsShown;
@Appearance
@@ -359,14 +350,6 @@ final class NavigationBarController {
});
}
- private boolean imeDrawsImeNavBar() {
- final Resources resources = mService.getResources();
- if (resources == null) {
- return false;
- }
- return resources.getBoolean(com.android.internal.R.bool.config_imeDrawsImeNavBar);
- }
-
@Override
public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
final Window window = softInputWindow.getWindow();
@@ -379,27 +362,6 @@ final class NavigationBarController {
if (mDestroyed) {
return;
}
- mImeDrawsImeNavBar = imeDrawsImeNavBar();
- if (mSystemOverlayChangedReceiver == null) {
- final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
- intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
- intentFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
- mSystemOverlayChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mDestroyed) {
- return;
- }
- mImeDrawsImeNavBar = imeDrawsImeNavBar();
- if (mImeDrawsImeNavBar) {
- installNavigationBarFrameIfNecessary();
- } else {
- uninstallNavigationBarFrameIfNecessary();
- }
- }
- };
- mService.registerReceiver(mSystemOverlayChangedReceiver, intentFilter);
- }
installNavigationBarFrameIfNecessary();
}
@@ -412,10 +374,6 @@ final class NavigationBarController {
mTintAnimator.cancel();
mTintAnimator = null;
}
- if (mSystemOverlayChangedReceiver != null) {
- mService.unregisterReceiver(mSystemOverlayChangedReceiver);
- mSystemOverlayChangedReceiver = null;
- }
mDestroyed = true;
}
@@ -448,28 +406,43 @@ final class NavigationBarController {
}
@Override
- public void setShouldShowImeSwitcherWhenImeIsShown(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
if (mDestroyed) {
return;
}
- if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) {
- return;
- }
+
+ final boolean imeDrawsImeNavBar =
+ (navButtonFlags & InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR) != 0;
+ final boolean shouldShowImeSwitcherWhenImeIsShown =
+ (navButtonFlags & InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN)
+ != 0;
+
+ mImeDrawsImeNavBar = imeDrawsImeNavBar;
+ final boolean prevShouldShowImeSwitcherWhenImeIsShown =
+ mShouldShowImeSwitcherWhenImeIsShown;
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
- if (mNavigationBarFrame == null) {
- return;
- }
- final NavigationBarView navigationBarView =
- mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
- if (navigationBarView == null) {
- return;
+ if (imeDrawsImeNavBar) {
+ installNavigationBarFrameIfNecessary();
+ if (mNavigationBarFrame == null) {
+ return;
+ }
+ if (mShouldShowImeSwitcherWhenImeIsShown
+ == prevShouldShowImeSwitcherWhenImeIsShown) {
+ return;
+ }
+ final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
+ NavigationBarView.class::isInstance);
+ if (navigationBarView == null) {
+ return;
+ }
+ final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
+ navigationBarView.setNavigationIconHints(hints);
+ } else {
+ uninstallNavigationBarFrameIfNecessary();
}
- final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
- | (shouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
- navigationBarView.setNavigationIconHints(hints);
}
@Override
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 0ce9c70569b5..731d1ba78299 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -142,9 +142,13 @@ public final class Tag implements Parcelable {
mTagService = tagService;
mConnectedTechnology = -1;
+ mCookie = SystemClock.elapsedRealtime();
+
+ if (tagService == null) {
+ return;
+ }
try {
- mCookie = SystemClock.elapsedRealtime();
tagService.setTagUpToDate(mCookie);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -370,6 +374,10 @@ public final class Tag implements Parcelable {
/** @hide */
@UnsupportedAppUsage
public INfcTag getTagService() {
+ if (mTagService == null) {
+ return null;
+ }
+
try {
if (!mTagService.isTagUpToDate(mCookie)) {
String id_str = "";
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 4e0995fb8792..db5bc7024e0b 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -47,7 +47,13 @@ public class ExternalVibration implements Parcelable {
this(uid, pkg, attrs, controller, new Binder());
}
- private ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+ /**
+ * Full constructor, but exposed to construct the ExternalVibration with an explicit binder
+ * token (for mocks).
+ *
+ * @hide
+ */
+ public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
@NonNull IExternalVibrationController controller, @NonNull IBinder token) {
mUid = uid;
mPkg = Preconditions.checkNotNull(pkg);
@@ -166,7 +172,7 @@ public class ExternalVibration implements Parcelable {
+ "pkg=" + mPkg + ", "
+ "attrs=" + mAttrs + ", "
+ "controller=" + mController
- + "token=" + mController
+ + "token=" + mToken
+ "}";
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
new file mode 100644
index 000000000000..00734c806b7e
--- /dev/null
+++ b/core/java/android/os/IpcDataCache.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.PropertyInvalidatedCache;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally retrieved
+ * across interprocess communication. Imagine that you've written a user birthday information
+ * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
+ * binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ * int month;
+ * int day;
+ * }
+ * interface IUserBirthdayService {
+ * Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ * private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ * {@literal @}Override
+ * public synchronized Birthday getUserBirthday(int userId) {
+ * return mUidToBirthday.get(userId);
+ * }
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * public Birthday getUserBirthday(int userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call to
+ * the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays change
+ * infrequently.
+ *
+ * IpcDataCache is part of a pattern for optimizing this kind of information-querying code. Using
+ * {@code IpcDataCache}, you'd write the client this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * };
+ * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
+ * private static final String BDAY_API = "getUserBirthday";
+ * private final IpcDataCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * IpcDataCache&lt;Integer, Birthday%&gt;(
+ * BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API, BDAY_API, mBirthdayQuery);
+ *
+ * public void disableUserBirthdayCache() {
+ * mBirthdayCache.disableForCurrentProcess();
+ * }
+ * public void invalidateUserBirthdayCache() {
+ * mBirthdayCache.invalidateCache();
+ * }
+ * public Birthday getUserBirthday(int userId) {
+ * return mBirthdayCache.query(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
+ * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
+ * string is permitted. The third parameters is the name of the API being cached; this, too, can
+ * any value. The fourth is the name of the cache. The cache is usually named after th API.
+ * Some things you must know about the three strings:
+ * <list>
+ * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * Usually, the SELinux rules permit a process to write a system property (and therefore
+ * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}. This means that
+ * although the cache can be constructed with any module string, whatever string is chosen must be
+ * consistent with the SELinux configuration.
+ * <ul> The API name can be any string of alphanumeric characters. All caches with the same API
+ * are invalidated at the same time. If a server supports several caches and all are invalidated
+ * in common, then it is most efficient to assign the same API string to every cache.
+ * <ul> The cache name can be any string. In debug output, the name is used to distiguish between
+ * caches with the same API name. The cache name is also used when disabling caches in the
+ * current process. So, invalidation is based on the module+api but disabling (which is generally
+ * a once-per-process operation) is based on the cache name.
+ * </list>
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ * ...
+ * public UserBirthdayServiceImpl() {
+ * ...
+ * ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ *
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * The call to {@code IpcDataCache.invalidateCache()} guarantees that all clients will re-fetch
+ * birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use of
+ * IpcDataCache in this idiomatic way introduces no new race conditions.
+ *
+ * IpcDataCache has a few other features for doing things like incremental enhancement of cached
+ * values and invalidation of multiple caches (that all share the same property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In this
+ * local case, there's no IPC, so use of the cache is (depending on exact circumstance)
+ * unnecessary.
+ *
+ * There may be queries for which it is more efficient to bypass the cache than to cache the
+ * result. This would be true, for example, if some queries would require frequent cache
+ * invalidation while other queries require infrequent invalidation. To expand on the birthday
+ * example, suppose that there is a userId that signifies "the next birthday". When passed this
+ * userId, the server returns the next birthday among all users - this value changes as time
+ * advances. The userId value can be cached, but the cache must be invalidated whenever a
+ * birthday occurs, and this invalidates all birthdays. If there is a large number of users,
+ * invalidation will happen so often that the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ * new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * public boolean shouldBypassQuery(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
+ * ...
+ * }
+ * </pre>
+ *
+ * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
+ * particular query. The {@code shouldBypassQuery()} method is not abstract and the default
+ * implementation returns false.
+ *
+ * For security, there is a allowlist of processes that are allowed to invalidate a cache. The
+ * allowlist includes normal runtime processes but does not include test processes. Test
+ * processes must call {@code IpcDataCache.disableForTestMode()} to disable all cache activity in
+ * that process.
+ *
+ * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
+ *
+ * To test a binder cache, create one or more tests that exercise the binder method. This should
+ * be done twice: once with production code and once with a special image that sets {@code DEBUG}
+ * and {@code VERIFY} true. In the latter case, verify that no cache inconsistencies are
+ * reported. If a cache inconsistency is reported, however, it might be a false positive. This
+ * happens if the server side data can be read and written non-atomically with respect to cache
+ * invalidation.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ * @hide
+ */
+@TestApi
+@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static abstract class QueryHandler<Q,R>
+ extends PropertyInvalidatedCache.QueryHandler<Q,R> {
+ /**
+ * Compute a result given a query. The semantics are those of Functor.
+ */
+ public abstract @Nullable R apply(@NonNull Q query);
+
+ /**
+ * Return true if a query should not use the cache. The default implementation
+ * always uses the cache.
+ */
+ public boolean shouldBypassCache(@NonNull Q query) {
+ return false;
+ }
+ };
+
+ /**
+ * The module used for unit tests and cts tests. It is expected that no process in
+ * the system has permissions to write properties with this module.
+ * @hide
+ */
+ @TestApi
+ public static final String MODULE_TEST = PropertyInvalidatedCache.MODULE_TEST;
+
+ /**
+ * The module used for system server/framework caches. This is not visible outside
+ * the system processes.
+ * @hide
+ */
+ @TestApi
+ public static final String MODULE_SYSTEM = PropertyInvalidatedCache.MODULE_SYSTEM;
+
+ /**
+ * The module used for bluetooth caches.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static final String MODULE_BLUETOOTH = PropertyInvalidatedCache.MODULE_BLUETOOTH;
+
+ /**
+ * Make a new property invalidated cache. The key is computed from the module and api
+ * parameters.
+ *
+ * @param maxEntries Maximum number of entries to cache; LRU discard
+ * @param module The module under which the cache key should be placed.
+ * @param api The api this cache front-ends. The api must be a Java identifier but
+ * need not be an actual api.
+ * @param cacheName Name of this cache in debug and dumpsys
+ * @param computer The code to compute values that are not in the cache.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public IpcDataCache(int maxEntries, @NonNull String module, @NonNull String api,
+ @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
+ super(maxEntries, module, api, cacheName, computer);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public void disableForCurrentProcess() {
+ super.disableForCurrentProcess();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void disableForCurrentProcess(@NonNull String cacheName) {
+ PropertyInvalidatedCache.disableForCurrentProcess(cacheName);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public @Nullable Result query(@NonNull Query query) {
+ return super.query(query);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @Override
+ public void invalidateCache() {
+ super.invalidateCache();
+ }
+
+ /**
+ * Invalidate caches in all processes that are keyed for the module and api.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void invalidateCache(@NonNull String module, @NonNull String api) {
+ PropertyInvalidatedCache.invalidateCache(module, api);
+ }
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9428ac965f84..e06e7326a860 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -257,6 +257,14 @@ public class Process {
public static final int UWB_UID = 1083;
/**
+ * Defines a virtual UID that is used to aggregate data related to SDK sandbox UIDs.
+ * {@see SdkSandboxManager}
+ * @hide
+ */
+ @TestApi
+ public static final int SDK_SANDBOX_VIRTUAL_UID = 1090;
+
+ /**
* GID that corresponds to the INTERNET permission.
* Must match the value of AID_INET.
* @hide
@@ -920,7 +928,7 @@ public class Process {
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
- public static final int sdkSandboxToAppUid(int uid) {
+ public static final int getAppUidForSdkSandboxUid(int uid) {
return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 722cdbc620a7..7832dccd93a5 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -54,13 +54,13 @@ interface IStorageManager {
*/
void shutdown(IStorageShutdownObserver observer) = 19;
/**
- * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and
- * only allows the calling process's UID access to the contents.
- * StorageManagerService will call back to the supplied IObbActionListener to inform
- * it of the terminal state of the call.
+ * Mounts an Opaque Binary Blob (OBB). Only allows the calling process's UID
+ * access to the contents. StorageManagerService will call back to the
+ * supplied IObbActionListener to inform it of the terminal state of the
+ * call.
*/
- void mountObb(in String rawPath, in String canonicalPath, in String key,
- IObbActionListener token, int nonce, in ObbInfo obbInfo) = 21;
+ void mountObb(in String rawPath, in String canonicalPath, IObbActionListener token,
+ int nonce, in ObbInfo obbInfo) = 21;
/**
* Unmounts an Opaque Binary Blob (OBB). When the force flag is specified,
* any program using it will be forcibly killed to unmount the image.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index dbf042063364..b501730f1eeb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -680,9 +680,7 @@ public class StorageManager {
}
/**
- * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
- * specified, it is supplied to the mounting process to be used in any
- * encryption used in the OBB.
+ * Mount an Opaque Binary Blob (OBB) file.
* <p>
* The OBB will remain mounted for as long as the StorageManager reference
* is held by the application. As soon as this reference is lost, the OBBs
@@ -695,19 +693,22 @@ public class StorageManager {
* application's OBB that shares its UID.
*
* @param rawPath the path to the OBB file
- * @param key secret used to encrypt the OBB; may be <code>null</code> if no
- * encryption was used on the OBB.
+ * @param key must be <code>null</code>. Previously, some Android device
+ * implementations accepted a non-<code>null</code> key to mount
+ * an encrypted OBB file. However, this never worked reliably and
+ * is no longer supported.
* @param listener will receive the success or failure of the operation
* @return whether the mount call was successfully queued or not
*/
public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+ Preconditions.checkArgument(key == null, "mounting encrypted OBBs is no longer supported");
Preconditions.checkNotNull(listener, "listener cannot be null");
try {
final String canonicalPath = new File(rawPath).getCanonicalPath();
final int nonce = mObbActionListener.addListener(listener);
- mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce,
+ mStorageManager.mountObb(rawPath, canonicalPath, mObbActionListener, nonce,
getObbInfo(canonicalPath));
return true;
} catch (IOException e) {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index db622d39b785..1fd75e610097 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1341,7 +1341,9 @@ public class DreamService extends Service implements Window.Callback {
public void onViewDetachedFromWindow(View v) {
if (mActivity == null || !mActivity.isChangingConfigurations()) {
// Only stop the dream if the view is not detached by relaunching
- // activity for configuration changes.
+ // activity for configuration changes. It is important to also clear
+ // the window reference in order to fully release the DreamActivity.
+ mWindow = null;
mActivity = null;
finish();
}
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
index f8318054ddfc..489a5f62b49d 100644
--- a/core/java/android/service/dreams/OWNERS
+++ b/core/java/android/service/dreams/OWNERS
@@ -1,3 +1,8 @@
# Bug component: 78010
+brycelee@google.com
dsandler@google.com
+galinap@google.com
+jjaggi@google.com
+michaelwr@google.com
+santoscordon@google.com
diff --git a/core/java/android/service/games/GameScreenshotResult.java b/core/java/android/service/games/GameScreenshotResult.java
index ae76e08c7971..5490aef0e225 100644
--- a/core/java/android/service/games/GameScreenshotResult.java
+++ b/core/java/android/service/games/GameScreenshotResult.java
@@ -18,8 +18,6 @@ package android.service.games;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,9 +28,7 @@ import java.util.Objects;
/**
* Result object for calls to {@link IGameSessionController#takeScreenshot}.
*
- * It includes a status (see {@link #getStatus}) and, if the status is
- * {@link #GAME_SCREENSHOT_SUCCESS} an {@link android.graphics.Bitmap} result (see {@link
- * #getBitmap}).
+ * It includes a status only (see {@link #getStatus}).
*
* @hide
*/
@@ -54,8 +50,7 @@ public final class GameScreenshotResult implements Parcelable {
/**
* Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} was
- * successful and an {@link android.graphics.Bitmap} result should be available by calling
- * {@link #getBitmap}.
+ * successful.
*
* @hide
*/
@@ -81,9 +76,7 @@ public final class GameScreenshotResult implements Parcelable {
new Parcelable.Creator<GameScreenshotResult>() {
@Override
public GameScreenshotResult createFromParcel(Parcel source) {
- return new GameScreenshotResult(
- source.readInt(),
- source.readParcelable(null, Bitmap.class));
+ return new GameScreenshotResult(source.readInt());
}
@Override
@@ -95,14 +88,11 @@ public final class GameScreenshotResult implements Parcelable {
@GameScreenshotStatus
private final int mStatus;
- @Nullable
- private final Bitmap mBitmap;
-
/**
- * Creates a successful {@link GameScreenshotResult} with the provided bitmap.
+ * Creates a successful {@link GameScreenshotResult}.
*/
- public static GameScreenshotResult createSuccessResult(@NonNull Bitmap bitmap) {
- return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS, bitmap);
+ public static GameScreenshotResult createSuccessResult() {
+ return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS);
}
/**
@@ -110,12 +100,11 @@ public final class GameScreenshotResult implements Parcelable {
* {@link #GAME_SCREENSHOT_ERROR_INTERNAL_ERROR} status.
*/
public static GameScreenshotResult createInternalErrorResult() {
- return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, null);
+ return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR);
}
- private GameScreenshotResult(@GameScreenshotStatus int status, @Nullable Bitmap bitmap) {
+ private GameScreenshotResult(@GameScreenshotStatus int status) {
this.mStatus = status;
- this.mBitmap = bitmap;
}
@Override
@@ -126,7 +115,6 @@ public final class GameScreenshotResult implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mStatus);
- dest.writeParcelable(mBitmap, flags);
}
@GameScreenshotStatus
@@ -134,29 +122,12 @@ public final class GameScreenshotResult implements Parcelable {
return mStatus;
}
- /**
- * Gets the {@link Bitmap} result from a successful screenshot attempt.
- *
- * @return The bitmap.
- * @throws IllegalStateException if this method is called when {@link #getStatus} does not
- * return {@link #GAME_SCREENSHOT_SUCCESS}.
- */
- @NonNull
- public Bitmap getBitmap() {
- if (mBitmap == null) {
- throw new IllegalStateException("Bitmap not available for failed screenshot result");
- }
- return mBitmap;
- }
-
@Override
public String toString() {
return "GameScreenshotResult{"
+ "mStatus="
+ mStatus
- + ", has bitmap='"
- + mBitmap != null ? "yes" : "no"
- + "\'}";
+ + "}";
}
@Override
@@ -170,12 +141,11 @@ public final class GameScreenshotResult implements Parcelable {
}
GameScreenshotResult that = (GameScreenshotResult) o;
- return mStatus == that.mStatus
- && Objects.equals(mBitmap, that.mBitmap);
+ return mStatus == that.mStatus;
}
@Override
public int hashCode() {
- return Objects.hash(mStatus, mBitmap);
+ return Objects.hash(mStatus);
}
}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index 468e087c941b..1548f5224572 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -29,7 +29,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
@@ -367,7 +366,7 @@ public abstract class GameSession {
}
/**
- * Interface for returning screenshot outcome from calls to {@link #takeScreenshot}.
+ * Interface for handling result of {@link #takeScreenshot}.
*/
public interface ScreenshotCallback {
@@ -402,18 +401,16 @@ public abstract class GameSession {
/**
* Called when taking the screenshot succeeded.
- *
- * @param bitmap The screenshot.
*/
- void onSuccess(@NonNull Bitmap bitmap);
+ void onSuccess();
}
/**
* Takes a screenshot of the associated game. For this call to succeed, the device screen
* must be turned on and the game task must be visible.
*
- * If the callback is called with {@link ScreenshotCallback#onSuccess}, the provided {@link
- * Bitmap} may be used.
+ * If the callback is called with {@link ScreenshotCallback#onSuccess}, the screenshot is
+ * taken successfully.
*
* If the callback is called with {@link ScreenshotCallback#onFailure}, the provided status
* code should be checked.
@@ -460,7 +457,7 @@ public abstract class GameSession {
@GameScreenshotResult.GameScreenshotStatus int status = result.getStatus();
switch (status) {
case GameScreenshotResult.GAME_SCREENSHOT_SUCCESS:
- callback.onSuccess(result.getBitmap());
+ callback.onSuccess();
break;
case GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR:
Slog.w(TAG, "Error taking screenshot");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3f29e3f3078e..a35a195c50b1 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -201,7 +201,12 @@ interface IWindowManager
boolean isKeyguardSecure(int userId);
void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message);
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)")
void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)")
void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
// Requires INTERACT_ACROSS_USERS_FULL permission
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 2b579d09d3a2..ef0c19c8dc02 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,20 +16,57 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.IBinder;
+import android.os.InputConfig;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
/**
- * Functions as a handle for a window that can receive input.
- * Enables the native input dispatcher to refer indirectly to the window manager's window state.
+ * Functions as a handle for a window that can receive input, and allows for the behavior of the
+ * input window to be configured.
* @hide
*/
public final class InputWindowHandle {
+
+ /**
+ * An internal annotation for all the {@link android.os.InputConfig} flags that can be
+ * specified to {@link #inputConfig} to control the behavior of an input window. Only the
+ * flags listed here are valid for use in Java.
+ *
+ * The default flag value is 0, which is what we expect for a normal application window. Adding
+ * a flag indicates that the window's behavior deviates from that of a normal application
+ * window.
+ *
+ * The flags are defined as an AIDL enum to keep it in sync with native code.
+ * {@link android.os.InputConfig} flags that are not listed here should not be used in Java, and
+ * are only meant to be used in native code.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ InputConfig.DEFAULT,
+ InputConfig.NO_INPUT_CHANNEL,
+ InputConfig.NOT_FOCUSABLE,
+ InputConfig.NOT_TOUCHABLE,
+ InputConfig.PREVENT_SPLITTING,
+ InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
+ InputConfig.IS_WALLPAPER,
+ InputConfig.PAUSE_DISPATCHING,
+ InputConfig.TRUSTED_OVERLAY,
+ InputConfig.WATCH_OUTSIDE_TOUCH,
+ InputConfig.SLIPPERY,
+ InputConfig.DISABLE_USER_ACTIVITY,
+ InputConfig.SPY,
+ InputConfig.INTERCEPTS_STYLUS,
+ })
+ public @interface InputConfigFlags {}
+
// Pointer to the native input window handle.
// This field is lazily initialized via JNI.
@SuppressWarnings("unused")
@@ -56,7 +93,8 @@ public final class InputWindowHandle {
// The window name.
public String name;
- // Window layout params attributes. (WindowManager.LayoutParams)
+ // Window layout params attributes. (WindowManager.LayoutParams)
+ // These values do not affect any input configurations. Use {@link #inputConfig} instead.
public int layoutParamsFlags;
public int layoutParamsType;
@@ -78,20 +116,9 @@ public final class InputWindowHandle {
// Window touchable region.
public final Region touchableRegion = new Region();
- // Window is visible.
- public boolean visible;
-
- // Window can be focused.
- public boolean focusable;
-
- // Window has wallpaper. (window is the current wallpaper target)
- public boolean hasWallpaper;
-
- // Input event dispatching is paused.
- public boolean paused;
-
- // Window is trusted overlay.
- public boolean trustedOverlay;
+ // Flags that specify the behavior of this input window. See {@link #InputConfigFlags}.
+ @InputConfigFlags
+ public int inputConfig;
// What effect this window has on touch occlusion if it lets touches pass through
// By default windows will block touches if they are untrusted and from a different UID due to
@@ -105,25 +132,21 @@ public final class InputWindowHandle {
// Owner package of the window
public String packageName;
- // Window input features.
- public int inputFeatures;
-
- // Display this input is on.
+ // Display this input window is on.
public int displayId;
/**
- * Crops the touchable region to the bounds of the surface provided.
+ * Crops the {@link #touchableRegion} to the bounds of the surface provided.
*
- * This can be used in cases where the window is not
- * {@link android.view.WindowManager#FLAG_NOT_TOUCH_MODAL} but should be constrained to the
- * bounds of a parent window. That is the window should receive touch events outside its
- * window but be limited to its stack bounds, such as in the case of split screen.
+ * This can be used in cases where the window should be constrained to the bounds of a parent
+ * window. That is, the window should receive touch events outside its window frame, but be
+ * limited to its stack bounds, such as in the case of split screen.
*/
public WeakReference<SurfaceControl> touchableRegionSurfaceControl = new WeakReference<>(null);
/**
- * Replace {@link touchableRegion} with the bounds of {@link touchableRegionSurfaceControl}. If
- * the handle is {@code null}, the bounds of the surface associated with this window is used
+ * Replace {@link #touchableRegion} with the bounds of {@link #touchableRegionSurfaceControl}.
+ * If the handle is {@code null}, the bounds of the surface associated with this window is used
* as the touchable region.
*/
public boolean replaceTouchableRegionWithCrop;
@@ -147,7 +170,6 @@ public final class InputWindowHandle {
.append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
.append(frameRight).append(",").append(frameBottom).append("]")
.append(", touchableRegion=").append(touchableRegion)
- .append(", visible=").append(visible)
.append(", scaleFactor=").append(scaleFactor)
.append(", transform=").append(transform)
.append(", windowToken=").append(windowToken)
@@ -165,11 +187,11 @@ public final class InputWindowHandle {
}
/**
- * Set the window touchable region to the bounds of {@link touchableRegionBounds} ignoring any
- * touchable region provided.
+ * Set the window's touchable region to the bounds of {@link #touchableRegionSurfaceControl}
+ * and ignore the value of {@link #touchableRegion}.
*
- * @param bounds surface to set the touchable region to. Set to {@code null} to set the bounds
- * to the current surface.
+ * @param bounds surface to set the touchable region to. Set to {@code null} to set the
+ * touchable region as the current surface bounds.
*/
public void replaceTouchableRegionWithCrop(@Nullable SurfaceControl bounds) {
setTouchableRegionCrop(bounds);
@@ -195,4 +217,17 @@ public final class InputWindowHandle {
window = IWindow.Stub.asInterface(windowToken);
return window;
}
+
+ /**
+ * Set the provided inputConfig flag values.
+ * @param inputConfig the flag values to change
+ * @param value the provided flag values are set when true, and cleared when false
+ */
+ public void setInputConfig(@InputConfigFlags int inputConfig, boolean value) {
+ if (value) {
+ this.inputConfig |= inputConfig;
+ return;
+ }
+ this.inputConfig &= ~inputConfig;
+ }
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0ef585478346..cc93adc32541 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1877,7 +1877,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 5775944b4e97..ebc409e470e9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -1104,6 +1104,7 @@ public class ViewConfiguration {
* clear, or -1 if Views should not set themselves as preferred to keep clear.
* @hide
*/
+ @TestApi
public int getPreferKeepClearForFocusDelay() {
return mPreferKeepClearForFocusDelay;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dbfae46a6e72..45169efd7d35 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -106,7 +106,6 @@ import android.graphics.Region;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.IInputConstants;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -3360,8 +3359,7 @@ public interface WindowManager extends ViewManager {
*
* @hide
*/
- public static final int INPUT_FEATURE_NO_INPUT_CHANNEL =
- IInputConstants.InputFeature.NO_INPUT_CHANNEL;
+ public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 1 << 0;
/**
* When this window has focus, does not call user activity for all input events so
@@ -3374,58 +3372,43 @@ public interface WindowManager extends ViewManager {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY =
- IInputConstants.InputFeature.DISABLE_USER_ACTIVITY;
+ public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY = 1 << 1;
/**
* An input spy window. This window will receive all pointer events within its touchable
- * area, but will will not stop events from being sent to other windows below it in z-order.
+ * area, but will not stop events from being sent to other windows below it in z-order.
* An input event will be dispatched to all spy windows above the top non-spy window at the
* event's coordinates.
- * @hide
- */
- public static final int INPUT_FEATURE_SPY =
- IInputConstants.InputFeature.SPY;
-
- /**
- * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
- * to receive events from a stylus device within its touchable region. All other pointer
- * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
- *
- * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
- * not set.
- *
- * The window must be a trusted overlay to use this input feature.
- *
- * @see #FLAG_NOT_TOUCHABLE
*
* @hide
*/
- public static final int INPUT_FEATURE_INTERCEPTS_STYLUS =
- IInputConstants.InputFeature.INTERCEPTS_STYLUS;
+ @RequiresPermission(permission.MONITOR_INPUT)
+ public static final int INPUT_FEATURE_SPY = 1 << 2;
/**
* An internal annotation for flags that can be specified to {@link #inputFeatures}.
*
+ * NOTE: These are not the same as {@link android.os.InputConfig} flags.
+ *
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "INPUT_FEATURE_" }, value = {
- INPUT_FEATURE_NO_INPUT_CHANNEL,
- INPUT_FEATURE_DISABLE_USER_ACTIVITY,
- INPUT_FEATURE_SPY,
- INPUT_FEATURE_INTERCEPTS_STYLUS,
+ @IntDef(flag = true, prefix = {"INPUT_FEATURE_"}, value = {
+ INPUT_FEATURE_NO_INPUT_CHANNEL,
+ INPUT_FEATURE_DISABLE_USER_ACTIVITY,
+ INPUT_FEATURE_SPY,
})
- public @interface InputFeatureFlags {}
+ public @interface InputFeatureFlags {
+ }
/**
- * Control special features of the input subsystem.
+ * Control a set of features of the input subsystem that are exposed to the app process.
+ *
+ * WARNING: Do NOT use {@link android.os.InputConfig} flags! This must be set to flag values
+ * included in {@link InputFeatureFlags}.
*
- * @see #INPUT_FEATURE_NO_INPUT_CHANNEL
- * @see #INPUT_FEATURE_DISABLE_USER_ACTIVITY
- * @see #INPUT_FEATURE_SPY
- * @see #INPUT_FEATURE_INTERCEPTS_STYLUS
* @hide
+ * @see InputFeatureFlags
*/
@InputFeatureFlags
@UnsupportedAppUsage
@@ -4845,10 +4828,6 @@ public interface WindowManager extends ViewManager {
inputFeatures &= ~INPUT_FEATURE_SPY;
features.add("INPUT_FEATURE_SPY");
}
- if ((inputFeatures & INPUT_FEATURE_INTERCEPTS_STYLUS) != 0) {
- inputFeatures &= ~INPUT_FEATURE_INTERCEPTS_STYLUS;
- features.add("INPUT_FEATURE_INTERCEPTS_STYLUS");
- }
if (inputFeatures != 0) {
features.add(Integer.toHexString(inputFeatures));
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index aeef76c3d048..0008aa64efa4 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4487,8 +4487,8 @@ public class AccessibilityNodeInfo implements Parcelable {
case R.id.accessibilityActionDragDrop:
return "ACTION_DROP";
default: {
- if (action == R.id.accessibilityActionShowSuggestions) {
- return "ACTION_SHOW_SUGGESTIONS";
+ if (action == R.id.accessibilityActionShowTextSuggestions) {
+ return "ACTION_SHOW_TEXT_SUGGESTIONS";
}
return "ACTION_UNKNOWN";
}
@@ -5189,34 +5189,10 @@ public class AccessibilityNodeInfo implements Parcelable {
new AccessibilityAction(R.id.accessibilityActionDragCancel);
/**
- * Action to perform a left swipe.
- */
- @NonNull public static final AccessibilityAction ACTION_SWIPE_LEFT =
- new AccessibilityAction(R.id.accessibilityActionSwipeLeft);
-
- /**
- * Action to perform a right swipe.
- */
- @NonNull public static final AccessibilityAction ACTION_SWIPE_RIGHT =
- new AccessibilityAction(R.id.accessibilityActionSwipeRight);
-
- /**
- * Action to perform an up swipe.
- */
- @NonNull public static final AccessibilityAction ACTION_SWIPE_UP =
- new AccessibilityAction(R.id.accessibilityActionSwipeUp);
-
- /**
- * Action to perform a down swipe.
- */
- @NonNull public static final AccessibilityAction ACTION_SWIPE_DOWN =
- new AccessibilityAction(R.id.accessibilityActionSwipeDown);
-
- /**
* Action to show suggestions for editable text.
*/
- @NonNull public static final AccessibilityAction ACTION_SHOW_SUGGESTIONS =
- new AccessibilityAction(R.id.accessibilityActionShowSuggestions);
+ @NonNull public static final AccessibilityAction ACTION_SHOW_TEXT_SUGGESTIONS =
+ new AccessibilityAction(R.id.accessibilityActionShowTextSuggestions);
private final int mActionId;
private final CharSequence mLabel;
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 69ad739608b2..fd336a27bb67 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -31,6 +31,7 @@ import android.view.MotionEvent;
import android.view.View;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
@@ -105,14 +106,13 @@ public interface InputMethod {
* current IME.
* @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
* @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME.
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags The initial state of {@link InputMethodNavButtonFlags}.
* @hide
*/
@MainThread
default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges,
- boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean stylusHwSupported, @InputMethodNavButtonFlags int navButtonFlags) {
attachToken(token);
}
@@ -231,8 +231,7 @@ public interface InputMethod {
* the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as
* long as your implementation of {@link InputMethod} relies on such
* IPCs
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -241,7 +240,7 @@ public interface InputMethod {
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
@@ -250,15 +249,13 @@ public interface InputMethod {
}
/**
- * Notifies that whether the IME should show the IME switcher or not is being changed.
+ * Notifies that {@link InputMethodNavButtonFlags} have been updated.
*
- * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be
- * shown while the IME is shown.
+ * @param navButtonFlags The new {@link InputMethodNavButtonFlags}.
* @hide
*/
@MainThread
- default void onShouldShowImeSwitcherWhenImeIsShownChanged(
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
}
/**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b00a3829f468..fbad38f27a23 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6681,7 +6681,13 @@ public class RemoteViews implements Parcelable, Filter {
opts = ActivityOptions.makeBasic();
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
- opts.setLaunchDisplayId(view.getDisplay().getDisplayId());
+ if (view.getDisplay() != null) {
+ opts.setLaunchDisplayId(view.getDisplay().getDisplayId());
+ } else {
+ // TODO(b/218409359): Remove once bug is fixed.
+ Log.w(LOG_TAG, "getLaunchOptions: view.getDisplay() is null!",
+ new Exception());
+ }
return Pair.create(intent, opts);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index c207af53fab7..b076d399d44a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12313,7 +12313,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
info.addAction(AccessibilityNodeInfo.ACTION_CUT);
}
if (canReplace()) {
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_SUGGESTIONS);
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TEXT_SUGGESTIONS);
}
if (canShare()) {
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
@@ -12634,7 +12635,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
default: {
// New ids have static blocks to assign values, so they can't be used in a case
// block.
- if (action == R.id.accessibilityActionShowSuggestions) {
+ if (action == R.id.accessibilityActionShowTextSuggestions) {
return isFocused() && canReplace() && onTextContextMenuItem(ID_REPLACE);
}
return super.performAccessibilityActionInternal(action, arguments);
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 022d05da8825..172456e4d0fa 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -52,7 +52,7 @@ interface ITaskOrganizerController {
/** Gets all root tasks on a display (ordered from top-to-bottom) */
List<ActivityManager.RunningTaskInfo> getRootTasks(int displayId, in int[] activityTypes);
- /** Get the root task which contains the current ime target */
+ /** Get the {@link WindowContainerToken} of the task which contains the current ime target */
WindowContainerToken getImeTarget(int display);
/**
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 3ec18dbe0ebc..8d88f80ff483 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -199,7 +199,7 @@ public class TaskOrganizer extends WindowOrganizer {
}
}
- /** Get the root task which contains the current ime target */
+ /** Get the {@link WindowContainerToken} of the task which contains the current ime target */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@Nullable
public WindowContainerToken getImeTarget(int display) {
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 5f82fb00ed75..5dbb551da681 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -28,6 +28,7 @@ import android.util.Log;
import android.view.IWindow;
import android.view.IWindowSession;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
@@ -185,35 +186,58 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
- private final OnBackInvokedCallback mCallback;
+ private final WeakReference<OnBackInvokedCallback> mCallback;
OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
- mCallback = callback;
- }
-
- @NonNull
- public OnBackInvokedCallback getCallback() {
- return mCallback;
+ mCallback = new WeakReference<>(callback);
}
@Override
public void onBackStarted() {
- Handler.getMain().post(() -> mCallback.onBackStarted());
+ Handler.getMain().post(() -> {
+ final OnBackInvokedCallback callback = mCallback.get();
+ if (callback == null) {
+ return;
+ }
+
+ callback.onBackStarted();
+ });
}
@Override
public void onBackProgressed(BackEvent backEvent) {
- Handler.getMain().post(() -> mCallback.onBackProgressed(backEvent));
+ Handler.getMain().post(() -> {
+ final OnBackInvokedCallback callback = mCallback.get();
+ if (callback == null) {
+ return;
+ }
+
+ callback.onBackProgressed(backEvent);
+ });
}
@Override
public void onBackCancelled() {
- Handler.getMain().post(() -> mCallback.onBackCancelled());
+ Handler.getMain().post(() -> {
+ final OnBackInvokedCallback callback = mCallback.get();
+ if (callback == null) {
+ return;
+ }
+
+ callback.onBackCancelled();
+ });
}
@Override
public void onBackInvoked() throws RemoteException {
- Handler.getMain().post(() -> mCallback.onBackInvoked());
+ Handler.getMain().post(() -> {
+ final OnBackInvokedCallback callback = mCallback.get();
+ if (callback == null) {
+ return;
+ }
+
+ callback.onBackInvoked();
+ });
}
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java b/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java
new file mode 100644
index 000000000000..728a42907cc4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodNavButtonFlags.java
@@ -0,0 +1,49 @@
+/*
+ * 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 static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * A set of flags notified from {@link com.android.server.inputmethod.InputMethodManagerService} to
+ * {@link android.inputmethodservice.InputMethodService} regarding how
+ * {@link android.inputmethodservice.NavigationBarController} should behave.
+ *
+ * <p>These flags will take effect when and only when
+ * {@link android.inputmethodservice.InputMethodService#canImeRenderGesturalNavButtons} returns
+ * {@code true}.</p>
+ */
+@Retention(SOURCE)
+@IntDef(flag = true, value = {
+ InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR,
+ InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN,
+})
+public @interface InputMethodNavButtonFlags {
+ /**
+ * When set, the IME process needs to render and handle the navigation bar buttons such as the
+ * back button and the IME switcher button.
+ */
+ int IME_DRAWS_IME_NAV_BAR = 1;
+ /**
+ * When set, the IME switcher icon needs to be shown on the navigation bar.
+ */
+ int SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN = 2;
+}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index ec95baae8c19..2784da00a02e 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -106,6 +106,7 @@ public class VpnConfig implements Parcelable {
public boolean allowIPv6;
public boolean isMetered = true;
public boolean requiresInternetValidation = false;
+ public boolean excludeLocalRoutes = false;
public Network[] underlyingNetworks;
public ProxyInfo proxyInfo;
@@ -133,6 +134,7 @@ public class VpnConfig implements Parcelable {
allowIPv6 = other.allowIPv6;
isMetered = other.isMetered;
requiresInternetValidation = other.requiresInternetValidation;
+ excludeLocalRoutes = other.excludeLocalRoutes;
underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
other.underlyingNetworks, other.underlyingNetworks.length) : null;
proxyInfo = other.proxyInfo;
@@ -192,6 +194,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowIPv6 ? 1 : 0);
out.writeInt(isMetered ? 1 : 0);
out.writeInt(requiresInternetValidation ? 1 : 0);
+ out.writeInt(excludeLocalRoutes ? 1 : 0);
out.writeTypedArray(underlyingNetworks, flags);
out.writeParcelable(proxyInfo, flags);
}
@@ -220,6 +223,7 @@ public class VpnConfig implements Parcelable {
config.allowIPv6 = in.readInt() != 0;
config.isMetered = in.readInt() != 0;
config.requiresInternetValidation = in.readInt() != 0;
+ config.excludeLocalRoutes = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
config.proxyInfo = in.readParcelable(null, android.net.ProxyInfo.class);
return config;
@@ -253,7 +257,8 @@ public class VpnConfig implements Parcelable {
.append(", allowIPv4=").append(allowIPv4)
.append(", allowIPv6=").append(allowIPv6)
.append(", isMetered=").append(isMetered)
- .append(", requiresInternetValidation").append(requiresInternetValidation)
+ .append(", requiresInternetValidation=").append(requiresInternetValidation)
+ .append(", excludeLocalRoutes=").append(excludeLocalRoutes)
.append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks))
.append(", proxyInfo=").append(proxyInfo)
.append("}")
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 37c96e71a0a8..74749cc9bf0b 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -100,7 +100,7 @@ public class TransitionAnimation {
// TODO (b/215515255): remove once we full migrate to shell transitions
private static final boolean SHELL_TRANSITIONS_ENABLED =
- SystemProperties.getBoolean("persist.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
private final Context mContext;
private final String mTag;
@@ -312,9 +312,9 @@ public class TransitionAnimation {
/** Load animation by attribute Id from android package. */
@Nullable
- public Animation loadDefaultAnimationAttr(int animAttr) {
+ public Animation loadDefaultAnimationAttr(int animAttr, boolean translucent) {
return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr,
- false /* translucent */);
+ translucent);
}
@Nullable
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9163b6d6215e..1d60c501a88b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -70,7 +70,7 @@ interface IStatusBarService
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
- void clearNotificationEffects();
+ oneway void clearNotificationEffects();
void onNotificationClick(String key, in NotificationVisibility nv);
void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationError(String pkg, String tag, int id,
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index d3c3917cd791..f4f438b1f601 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -11,8 +11,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
import android.graphics.Insets;
+import android.graphics.ParcelableColorSpace;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -26,6 +30,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.WindowManager;
+import java.util.Objects;
import java.util.function.Consumer;
public class ScreenshotHelper {
@@ -154,6 +159,72 @@ public class ScreenshotHelper {
};
}
+ /**
+ * Bundler used to convert between a hardware bitmap and a bundle without copying the internal
+ * content. This is expected to be used together with {@link #provideScreenshot} to handle a
+ * hardware bitmap as a screenshot.
+ */
+ public static final class HardwareBitmapBundler {
+ private static final String KEY_BUFFER = "bitmap_util_buffer";
+ private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";
+
+ private HardwareBitmapBundler() {
+ }
+
+ /**
+ * Creates a Bundle that represents the given Bitmap.
+ * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
+ * copies when passing across processes, only pass to processes you trust.
+ *
+ * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
+ * returned Bundle should be treated as a standalone object.
+ *
+ * @param bitmap to convert to bundle
+ * @return a Bundle representing the bitmap, should only be parsed by
+ * {@link #bundleToHardwareBitmap(Bundle)}
+ */
+ public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
+ if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
+ throw new IllegalArgumentException(
+ "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
+ }
+
+ // Bitmap assumes SRGB for null color space
+ ParcelableColorSpace colorSpace =
+ bitmap.getColorSpace() == null
+ ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
+ : new ParcelableColorSpace(bitmap.getColorSpace());
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
+ bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);
+
+ return bundle;
+ }
+
+ /**
+ * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
+ *
+ * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing
+ * this
+ * Bitmap on to any other source.
+ *
+ * @param bundle containing the bitmap
+ * @return a hardware Bitmap
+ */
+ public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
+ if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
+ throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
+ }
+
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE);
+
+ return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
+ colorSpace.getColorSpace());
+ }
+ }
+
private static final String TAG = "ScreenshotHelper";
// Time until we give up on the screenshot & show an error instead.
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index d2bc3442ed36..273c5f170812 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -37,8 +37,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
*/
oneway interface IInputMethod {
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
- int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown);
+ int configChanges, boolean stylusHwSupported, int navigationBarFlags);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
@@ -48,10 +47,9 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo attribute, boolean restarting,
- boolean shouldShowImeSwitcherWhenImeIsShown);
+ in EditorInfo attribute, boolean restarting, int navigationBarFlags);
- void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown);
+ void onNavButtonFlagsChanged(int navButtonFlags);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index f453d1f06e22..db92310f2a26 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -23,6 +23,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <binder/IPCThreadState.h>
+#include <ftl/cast.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfo.h>
#include <nativehelper/JNIHelp.h>
@@ -63,16 +64,11 @@ static struct {
jfieldID surfaceInset;
jfieldID scaleFactor;
jfieldID touchableRegion;
- jfieldID visible;
- jfieldID focusable;
- jfieldID hasWallpaper;
- jfieldID paused;
- jfieldID trustedOverlay;
jfieldID touchOcclusionMode;
jfieldID ownerPid;
jfieldID ownerUid;
jfieldID packageName;
- jfieldID inputFeatures;
+ jfieldID inputConfig;
jfieldID displayId;
jfieldID replaceTouchableRegionWithCrop;
WeakRefHandleField touchableRegionSurfaceControl;
@@ -162,64 +158,8 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.layoutParamsFlags = flags;
mInfo.layoutParamsType = type;
- using InputConfig = gui::WindowInfo::InputConfig;
- // Determine the value for each of the InputConfig flags. We rely on a switch statement and
- // -Wswitch-enum to give us a build error if we forget to explicitly handle an InputConfig flag.
- mInfo.inputConfig = InputConfig::NONE;
- InputConfig enumerationStart = InputConfig::NONE;
- switch (enumerationStart) {
- case InputConfig::NONE:
- FALLTHROUGH_INTENDED;
- case InputConfig::NOT_VISIBLE:
- if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.visible) == JNI_FALSE) {
- mInfo.inputConfig |= InputConfig::NOT_VISIBLE;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::NOT_FOCUSABLE:
- if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.focusable) == JNI_FALSE) {
- mInfo.inputConfig |= InputConfig::NOT_FOCUSABLE;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::NOT_TOUCHABLE:
- if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
- mInfo.inputConfig |= InputConfig::NOT_TOUCHABLE;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::PREVENT_SPLITTING:
- if (!flags.test(WindowInfo::Flag::SPLIT_TOUCH)) {
- mInfo.inputConfig |= InputConfig::PREVENT_SPLITTING;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER:
- if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.hasWallpaper) == JNI_TRUE) {
- mInfo.inputConfig |= InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::IS_WALLPAPER:
- if (type == WindowInfo::Type::WALLPAPER) {
- mInfo.inputConfig |= InputConfig::IS_WALLPAPER;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::PAUSE_DISPATCHING:
- if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.paused) == JNI_TRUE) {
- mInfo.inputConfig |= InputConfig::PAUSE_DISPATCHING;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::TRUSTED_OVERLAY:
- if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay) == JNI_TRUE) {
- mInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::WATCH_OUTSIDE_TOUCH:
- if (flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
- mInfo.inputConfig |= InputConfig::WATCH_OUTSIDE_TOUCH;
- }
- FALLTHROUGH_INTENDED;
- case InputConfig::SLIPPERY:
- if (flags.test(WindowInfo::Flag::SLIPPERY)) {
- mInfo.inputConfig |= InputConfig::SLIPPERY;
- }
- }
+ mInfo.inputConfig = static_cast<gui::WindowInfo::InputConfig>(
+ env->GetIntField(obj, gInputWindowHandleClassInfo.inputConfig));
mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
@@ -228,8 +168,6 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.ownerUid = env->GetIntField(obj,
gInputWindowHandleClassInfo.ownerUid);
mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
- mInfo.inputFeatures = static_cast<WindowInfo::Feature>(
- env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
@@ -359,17 +297,6 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.touchableRegion,
regionObj.get());
- using InputConfig = gui::WindowInfo::InputConfig;
- env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.visible,
- !windowInfo.inputConfig.test(InputConfig::NOT_VISIBLE));
- env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.focusable,
- !windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
- env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.hasWallpaper,
- windowInfo.inputConfig.test(InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
- env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.paused,
- windowInfo.inputConfig.test(InputConfig::PAUSE_DISPATCHING));
- env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.trustedOverlay,
- windowInfo.inputConfig.test(InputConfig::TRUSTED_OVERLAY));
env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
static_cast<int32_t>(windowInfo.touchOcclusionMode));
env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid);
@@ -377,8 +304,11 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn
ScopedLocalRef<jstring> packageName(env, env->NewStringUTF(windowInfo.packageName.data()));
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName,
packageName.get());
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputFeatures,
- static_cast<int32_t>(windowInfo.inputFeatures.get()));
+
+ const auto inputConfig = windowInfo.inputConfig.get();
+ static_assert(sizeof(inputConfig) == sizeof(int32_t));
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputConfig,
+ static_cast<int32_t>(inputConfig));
float transformVals[9];
for (int i = 0; i < 9; i++) {
@@ -481,19 +411,6 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegion, clazz,
"touchableRegion", "Landroid/graphics/Region;");
- GET_FIELD_ID(gInputWindowHandleClassInfo.visible, clazz,
- "visible", "Z");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.focusable, clazz, "focusable", "Z");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.hasWallpaper, clazz,
- "hasWallpaper", "Z");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.paused, clazz,
- "paused", "Z");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.trustedOverlay, clazz, "trustedOverlay", "Z");
-
GET_FIELD_ID(gInputWindowHandleClassInfo.touchOcclusionMode, clazz, "touchOcclusionMode", "I");
GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz,
@@ -505,8 +422,7 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.packageName, clazz, "packageName",
"Ljava/lang/String;");
- GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz,
- "inputFeatures", "I");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.inputConfig, clazz, "inputConfig", "I");
GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
"displayId", "I");
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 248db76da71d..0c05da551c8f 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -45,7 +45,7 @@ using android::zygote::ZygoteFailure;
// WARNING: Knows a little about the wire protocol used to communicate with Zygote.
// TODO: Fix error handling.
-constexpr size_t MAX_COMMAND_BYTES = 12200;
+constexpr size_t MAX_COMMAND_BYTES = 32768;
constexpr size_t NICE_NAME_BYTES = 50;
// A buffer optionally bundled with a file descriptor from which we can fill it.
@@ -273,8 +273,6 @@ class NativeCommandBuffer {
char mBuffer[MAX_COMMAND_BYTES];
};
-static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
-
static int buffersAllocd(0);
// Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 51e150e28437..57026d95ceb8 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -48,7 +48,6 @@ import "frameworks/base/core/proto/android/service/batterystats.proto";
import "frameworks/base/core/proto/android/service/diskstats.proto";
import "frameworks/base/core/proto/android/service/dropbox.proto";
import "frameworks/base/core/proto/android/service/graphicsstats.proto";
-import "frameworks/base/core/proto/android/service/netstats.proto";
import "frameworks/base/core/proto/android/service/notification.proto";
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/print.proto";
@@ -62,7 +61,8 @@ import "frameworks/base/core/proto/android/util/textdump.proto";
import "frameworks/base/core/proto/android/privacy.proto";
import "frameworks/base/core/proto/android/section.proto";
import "frameworks/base/proto/src/ipconnectivity.proto";
-import "packages/modules/Permission/service/proto/com/android/role/roleservice.proto";
+import "packages/modules/Connectivity/framework/proto/netstats.proto";
+import "packages/modules/Permission/service/proto/role_service.proto";
package android.os;
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
deleted file mode 100644
index ba2b6d6bd7e0..000000000000
--- a/core/proto/android/service/netstats.proto
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package android.service;
-
-option java_multiple_files = true;
-option java_outer_classname = "NetworkStatsServiceProto";
-
-// Represents dumpsys from NetworkStatsService (netstats).
-message NetworkStatsServiceDumpProto {
- repeated NetworkInterfaceProto active_interfaces = 1;
-
- repeated NetworkInterfaceProto active_uid_interfaces = 2;
-
- // Device level network stats, which may include non-IP layer traffic.
- optional NetworkStatsRecorderProto dev_stats = 3;
-
- // IP-layer traffic stats.
- optional NetworkStatsRecorderProto xt_stats = 4;
-
- // Per-UID network stats.
- optional NetworkStatsRecorderProto uid_stats = 5;
-
- // Per-UID, per-tag network stats, excluding the default tag (i.e. tag=0).
- optional NetworkStatsRecorderProto uid_tag_stats = 6;
-}
-
-// Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
-message NetworkInterfaceProto {
- // Name of the network interface (eg: wlan).
- optional string interface = 1;
-
- optional NetworkIdentitySetProto identities = 2;
-}
-
-// Corresponds to NetworkIdentitySet.
-message NetworkIdentitySetProto {
- repeated NetworkIdentityProto identities = 1;
-}
-
-// Corresponds to NetworkIdentity.
-message NetworkIdentityProto {
- // Constants from ConnectivityManager.TYPE_*.
- optional int32 type = 1;
-
- optional bool roaming = 4;
-
- optional bool metered = 5;
-
- optional bool default_network = 6;
-
- optional int32 oem_managed_network = 7;
-}
-
-// Corresponds to NetworkStatsRecorder.
-message NetworkStatsRecorderProto {
- optional int64 pending_total_bytes = 1;
-
- optional NetworkStatsCollectionProto complete_history = 2;
-}
-
-// Corresponds to NetworkStatsCollection.
-message NetworkStatsCollectionProto {
- repeated NetworkStatsCollectionStatsProto stats = 1;
-}
-
-// Corresponds to NetworkStatsCollection.mStats.
-message NetworkStatsCollectionStatsProto {
- optional NetworkStatsCollectionKeyProto key = 1;
-
- optional NetworkStatsHistoryProto history = 2;
-}
-
-// Corresponds to NetworkStatsCollection.Key.
-message NetworkStatsCollectionKeyProto {
- optional NetworkIdentitySetProto identity = 1;
-
- optional int32 uid = 2;
-
- optional int32 set = 3;
-
- optional int32 tag = 4;
-}
-
-// Corresponds to NetworkStatsHistory.
-message NetworkStatsHistoryProto {
- // Duration for this bucket in milliseconds.
- optional int64 bucket_duration_ms = 1;
-
- repeated NetworkStatsHistoryBucketProto buckets = 2;
-}
-
-// Corresponds to each bucket in NetworkStatsHistory.
-message NetworkStatsHistoryBucketProto {
- // Bucket start time in milliseconds since epoch.
- optional int64 bucket_start_ms = 1;
-
- optional int64 rx_bytes = 2;
-
- optional int64 rx_packets = 3;
-
- optional int64 tx_bytes = 4;
-
- optional int64 tx_packets = 5;
-
- optional int64 operations = 6;
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 434222ae8016..21baa0bcea97 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2937,7 +2937,7 @@
<!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature|role|setup"
+ android:protectionLevel="signature|role"
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
@@ -2946,6 +2946,10 @@
<permission android:name="android.permission.QUERY_ADMIN_POLICY"
android:protectionLevel="signature|role" />
+ <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
+ <permission android:name="android.permission.PROVISION_DEMO_DEVICE"
+ android:protectionLevel="signature|setup" />
+
<!-- @TestApi @hide Allows an application to reset the record of previous system update freeze
periods. -->
<permission android:name="android.permission.CLEAR_FREEZE_PERIOD"
@@ -3714,15 +3718,26 @@
android:protectionLevel="signature|privileged" />
<!-- ========================================= -->
- <!-- Permissions for SupplementalApi -->
+ <!-- Permissions for AdServices -->
<!-- ========================================= -->
<eat-comment />
- <!-- TODO(b/213488783): Update with correct names. -->
- <!-- Allows an application to access SupplementalApis. -->
- <permission android:name="android.permission.ACCESS_SUPPLEMENTAL_APIS"
- android:label="@string/permlab_accessSupplementalApi"
- android:description="@string/permdesc_accessSupplementalApi"
+ <!-- Allows an application to access AdServices Topics API. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS"
+ android:label="@string/permlab_accessAdServicesTopics"
+ android:description="@string/permdesc_accessAdServicesTopics"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to access AdServices Attribution APIs. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION"
+ android:label="@string/permlab_accessAdServicesAttribution"
+ android:description="@string/permdesc_accessAdServicesAttribution"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to access AdServices Custom Audiences APIs. -->
+ <permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCES"
+ android:label="@string/permlab_accessAdServicesCustomAudiences"
+ android:description="@string/permdesc_accessAdServicesCustomAudiences"
android:protectionLevel="normal" />
<!-- ==================================== -->
@@ -6653,10 +6668,9 @@
android:exported="false">
</activity>
- <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity"
- android:theme="@style/Theme.Dialog.Confirmation"
+ <activity android:name="com.android.server.logcat.LogAccessDialogActivity"
+ android:theme="@style/Theme.DeviceDefault.Dialog.Alert.DayNight"
android:excludeFromRecents="true"
- android:process=":ui"
android:label="@string/log_access_confirmation_title"
android:exported="false">
</activity>
diff --git a/core/res/res/drawable/grant_permissions_buttons_bottom.xml b/core/res/res/drawable/grant_permissions_buttons_bottom.xml
new file mode 100644
index 000000000000..a800f15b297e
--- /dev/null
+++ b/core/res/res/drawable/grant_permissions_buttons_bottom.xml
@@ -0,0 +1,23 @@
+<?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="rectangle">
+ <solid android:color="@android:color/system_accent1_100"/>
+ <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"
+ android:bottomLeftRadius="12dp" android:bottomRightRadius="12dp"/>
+</shape> \ No newline at end of file
diff --git a/core/res/res/drawable/grant_permissions_buttons_top.xml b/core/res/res/drawable/grant_permissions_buttons_top.xml
new file mode 100644
index 000000000000..2bf803e340ec
--- /dev/null
+++ b/core/res/res/drawable/grant_permissions_buttons_top.xml
@@ -0,0 +1,23 @@
+<?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="rectangle">
+ <solid android:color="@android:color/system_accent1_100"/>
+ <corners android:topLeftRadius="12dp" android:topRightRadius="12dp"
+ android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp"/>
+</shape> \ No newline at end of file
diff --git a/core/res/res/layout/log_access_user_consent_dialog_permission.xml b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
new file mode 100644
index 000000000000..bd7efbd59c33
--- /dev/null
+++ b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="380dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="24dp"
+ android:paddingBottom="24dp"
+ android:background="?attr/colorSurface">
+
+ <ImageView
+ android:id="@+id/log_access_image_view"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginBottom="16dp"
+ android:src="@drawable/ic_doc_document"
+ tools:layout_editor_absoluteX="148dp"
+ tools:layout_editor_absoluteY="35dp"
+ android:gravity="center" />
+
+
+ <TextView
+ android:id="@+id/log_access_dialog_title"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginBottom="32dp"
+ android:text="@string/log_access_confirmation_title"
+ android:textAppearance="?attr/textAppearanceLarge"
+ android:textColor="@android:color/system_neutral1_900"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@+id/log_access_dialog_body"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/log_access_confirmation_body"
+ android:textAppearance="@style/PrimaryAllowLogAccess"
+ android:gravity="center" />
+
+ <Button
+ android:id="@+id/log_access_dialog_allow_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/log_access_confirmation_allow"
+ style="@style/PermissionGrantButtonTop"
+ android:layout_marginBottom="5dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+
+ <Button
+ android:id="@+id/log_access_dialog_deny_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/log_access_confirmation_deny"
+ style="@style/PermissionGrantButtonBottom"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f16cdcfc595..0bdf71667783 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2723,7 +2723,7 @@
<string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
<!-- Whether force to enable telephony new data stack or not -->
- <bool name="config_force_enable_telephony_new_data_stack">false</bool>
+ <bool name="config_force_enable_telephony_new_data_stack">true</bool>
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
@@ -2931,6 +2931,11 @@
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
+ <!-- Settings intelligence package name -->
+ <string name="config_settingsIntelligencePackageName" translatable="false">
+ com.android.settings.intelligence
+ </string>
+
<!-- Flag indicating that the media framework should not allow changes or mute on any
stream or global volumes. -->
<bool name="config_useFixedVolume">false</bool>
@@ -4130,9 +4135,9 @@
This service must be trusted, as it can be activated without explicit consent of the user.
If no service with the specified name exists on the device, cloudsearch will be disabled.
Example: "com.android.intelligence/.CloudSearchService"
- config_defaultCloudSearchService is for the single provider case.
+ config_defaultCloudSearchServices is for the multiple provider case.
-->
- <string name="config_defaultCloudSearchService" translatable="false"></string>
+ <string-array name="config_defaultCloudSearchServices"></string-array>
<!-- The package name for the system's translation service.
This service must be trusted, as it can be activated without explicit consent of the user.
@@ -5262,9 +5267,9 @@
<bool name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable">true</bool>
<bool name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed">true</bool>
- <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">true</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">false</bool>
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
- <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+ <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">true</bool>
<bool name="config_cecSystemAudioControl_userConfigurable">true</bool>
<bool name="config_cecSystemAudioControlEnabled_allowed">true</bool>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 29eb0c0365b3..082acbe3443a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -270,20 +270,8 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL}. -->
<item type="id" name="accessibilityActionDragCancel" />
- <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_LEFT}. -->
- <item type="id" name="accessibilityActionSwipeLeft" />
-
- <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_RIGHT}. -->
- <item type="id" name="accessibilityActionSwipeRight" />
-
- <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_UP}. -->
- <item type="id" name="accessibilityActionSwipeUp" />
-
- <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_DOWN}. -->
- <item type="id" name="accessibilityActionSwipeDown" />
-
- <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_SUGGESTIONS}. -->
- <item type="id" name="accessibilityActionShowSuggestions" />
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_TEXT_SUGGESTIONS}. -->
+ <item type="id" name="accessibilityActionShowTextSuggestions" />
<!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
<item type="id" name="remote_views_next_child" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public-final.xml
index 3467e1b40470..19a484293b0a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public-final.xml
@@ -3211,8 +3211,6 @@
</staging-public-group-final>
<public type="attr" name="shouldUseDefaultUnfoldTransition" id="0x0101064c" />
- <public type="attr" name="lineBreakStyle" id="0x0101064d" />
- <public type="attr" name="lineBreakWordStyle" id="0x0101064e" />
<staging-public-group-final type="id" first-id="0x01fe0000">
<public name="accessibilityActionDragStart" />
@@ -3224,147 +3222,4 @@
<public type="id" name="accessibilityActionDragDrop" id="0x01020056" />
<public type="id" name="accessibilityActionDragCancel" id="0x01020057" />
- <!-- ===============================================================
- Resources added in version T of the platform
-
- NOTE: add <public> elements within a <staging-public-group> like so:
-
- <staging-public-group type="attr" first-id="0x01ff0000">
- <public name="exampleAttr1" />
- <public name="exampleAttr2" />
- </staging-public-group>
-
- To add a new <staging-public-group> block, find the id value for the
- last <staging-public-group> block defined for thie API level, and
- subtract 0x00010000 from it to get to the id of the new block.
-
- For example, if the block closest to the end of this file has an id
- of 0x01ee0000, the id of the new block should be 0x01ed0000
- (0x01ee0000 - 0x00010000 = 0x01ed0000).
- =============================================================== -->
- <eat-comment />
-
- <staging-public-group type="attr" first-id="0x01df0000">
- <public name="sharedUserMaxSdkVersion" />
- <public name="requiredSplitTypes" />
- <public name="splitTypes" />
- <public name="canDisplayOnRemoteDevices" />
- <public name="supportedTypes" />
- <public name="resetEnabledSettingsOnAppDataCleared" />
- <public name="supportsStylusHandwriting" />
- <public name="showClockAndComplications" />
- <!-- @hide @SystemApi -->
- <public name="gameSessionService" />
- <public name="supportsBatteryGameMode" />
- <public name="supportsPerformanceGameMode" />
- <public name="allowGameAngleDriver" />
- <public name="allowGameDownscaling" />
- <public name="allowGameFpsOverride" />
- <public name="localeConfig" />
- <public name="showBackground" />
- <public name="useTargetActivityForQuickAccess"/>
- <public name="inheritKeyStoreKeys" />
- <public name="preferKeepClear" />
- <public name="autoHandwritingEnabled" />
- <public name="fromExtendLeft" />
- <public name="fromExtendTop" />
- <public name="fromExtendRight" />
- <public name="fromExtendBottom" />
- <public name="toExtendLeft" />
- <public name="toExtendTop" />
- <public name="toExtendRight" />
- <public name="toExtendBottom" />
- <public name="tileService" />
- <public name="windowSplashScreenBehavior" />
- <public name="allowUntrustedActivityEmbedding" />
- <public name="knownActivityEmbeddingCerts" />
- <public name="intro" />
- <public name="enableOnBackInvokedCallback" />
- <public name="supportsInlineSuggestionsWithTouchExploration" />
- </staging-public-group>
-
- <staging-public-group type="id" first-id="0x01de0000">
- <public name="accessibilityActionSwipeLeft" />
- <public name="accessibilityActionSwipeRight" />
- <public name="accessibilityActionSwipeUp" />
- <public name="accessibilityActionSwipeDown" />
- <public name="accessibilityActionShowSuggestions" />
- <public name="inputExtractAction" />
- <public name="inputExtractAccessories" />
- </staging-public-group>
-
- <staging-public-group type="style" first-id="0x01dd0000">
- <public name="TextAppearance.DeviceDefault.Headline" />
- </staging-public-group>
-
- <staging-public-group type="string" first-id="0x01dc0000">
- <!-- @hide @SystemApi -->
- <public name="config_systemSupervision" />
- <!-- @hide @SystemApi -->
- <public name="config_devicePolicyManagement" />
- <!-- @hide @SystemApi -->
- <public name="config_systemAppProtectionService" />
- <!-- @hide @SystemApi @TestApi -->
- <public name="config_systemAutomotiveCalendarSyncManager" />
- <!-- @hide @SystemApi -->
- <public name="config_defaultAutomotiveNavigation" />
- </staging-public-group>
-
- <staging-public-group type="dimen" first-id="0x01db0000">
- </staging-public-group>
-
- <staging-public-group type="color" first-id="0x01da0000">
- </staging-public-group>
-
- <staging-public-group type="array" first-id="0x01d90000">
- <!-- @hide @SystemApi -->
- <public name="config_optionalIpSecAlgorithms" />
- </staging-public-group>
-
- <staging-public-group type="drawable" first-id="0x01d80000">
- </staging-public-group>
-
- <staging-public-group type="layout" first-id="0x01d70000">
- </staging-public-group>
-
- <staging-public-group type="anim" first-id="0x01d60000">
- </staging-public-group>
-
- <staging-public-group type="animator" first-id="0x01d50000">
- </staging-public-group>
-
- <staging-public-group type="interpolator" first-id="0x01d40000">
- </staging-public-group>
-
- <staging-public-group type="mipmap" first-id="0x01d30000">
- </staging-public-group>
-
- <staging-public-group type="integer" first-id="0x01d20000">
- </staging-public-group>
-
- <staging-public-group type="transition" first-id="0x01d10000">
- </staging-public-group>
-
- <staging-public-group type="raw" first-id="0x01d00000">
- </staging-public-group>
-
- <staging-public-group type="bool" first-id="0x01cf0000">
- <!-- @hide @TestApi -->
- <public name="config_preventImeStartupUnlessTextEditor" />
- <!-- @hide @SystemApi -->
- <public name="config_enableQrCodeScannerOnLockScreen" />
- </staging-public-group>
-
- <staging-public-group type="fraction" first-id="0x01ce0000">
- </staging-public-group>
-
- <!-- ===============================================================
- DO NOT ADD UN-GROUPED ITEMS HERE
-
- Any new items (attrs, styles, ids, etc.) *must* be added in a
- staging-public-group block, as the preceding comment explains.
- Items added outside of a group may have their value recalculated
- every time something new is added to this file.
- =============================================================== -->
-
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
new file mode 100644
index 000000000000..2dc17b8468c3
--- /dev/null
+++ b/core/res/res/values/public-staging.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Exposing a new resource:
+ To add a new entry, find the corresponding "staging-public-group" with the correct type for
+ your resource, and add a new entry to the BOTTOM of the list. This ensures that indexes
+ don't shift for previously added resources, and the new one will be appended to the end.
+
+ To add R.attr.exampleAttrName:
+ <staging-public-group type="attr" first-id="0x1ff0000">
+ <public name="previouslyAdded1"/>
+ <public name="previouslyAdded2"/>
+ <public name="exampleAttrName"/>
+ </staging-public-group>
+
+ Deleting a resource:
+ If a resource is no longer supported/used, it can be marked removed by renaming the
+ resource with a `removed_` prefix. This preserves the indexes of other resources so as not
+ to break apps that have compiled with their integers previously.
+
+ To remove R.attr.previouslyAdded2:
+ <staging-public-group type="attr" first-id="0x1ff0000">
+ <public name="previouslyAdded1"/>
+ <public name="removed_previouslyAdded2"/>
+ <public name="exampleAttrName"/>
+ </staging-public-group>
+
+ IMPORTANT: Deleting an entry is never allowed, even across branches or reverts. Please take
+ this into account before merging a change which edits this file. Small, isolated changes
+ which only add/remove resources is recommended to avoid reverts due to build/test failures.
+
+ Renaming a resource:
+ This is generally fine and can be done to the entry directly, with no other changes. But
+ note that any apps/tooling that resolve against resource names rather than IDs may break
+ as a result. This is uncommon, but not rare.
+
+ Finalizing a release's resources:
+ 1. $ANDROID_BUILD_TOP/frameworks/base/tools/aapt2/tools/finalize_res.py \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-staging.xml \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-final.xml
+ 2. Rename "NEXT" in the new public-staging.xml resources header to the next platform short
+ version code
+
+ Finalizing a release's resources (manually; only for reference):
+ 1. Delete all "staging-public-group" blocks for the release with no entries inside them
+ 2. Rename the remaining "staging-public-group" blocks for that release to
+ "staging-public-group-final"
+ 3. Cut them out this file and place at the bottom of public-final.xml; also move the
+ "Resources added in version ? of the platform" header
+ 4. Copy-paste all of the non-"removed_" resources outside of the staging blocks into being
+ siblings alongside them
+ 5. Assign them final public IDs in the form of
+ <public type="attr" name="exampleAttrName" id="0x0101088a" />
+ by finding the last ID for that type and incrementing the last 4 characters by 1 in
+ hexadecimal
+ 6. Back in this file, seed the next release's resources by adding "staging-public-group"
+ tags with their "first-id" value shifted by -0x00010000 from the lowest "first-id"
+ in the last used "staging-public-group-final"
+
+ Example:
+ Starting public-staging.xml:
+ <!\- ===============================================================
+ Resources added in version ? of the platform
+ =============================================================== -\>
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1"/>
+ <public name="removed_exampleAttr2"/>
+ <public name="exampleAttr3"/>
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01fe0000">
+ </staging-public-group>
+
+ Resulting public-final.xml:
+ <!\- ===============================================================
+ Resources added in version ? of the platform
+ =============================================================== -\>
+ <eat-comment />
+
+ <staging-public-group-final type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1"/>
+ <public name="removed_exampleAttr2"/>
+ <public name="exampleAttr3"/>
+ </staging-public-group-final>
+
+ <public type="id" name="exampleAttr1" id="0x0101088a"/>
+ <public type="id" name="exampleAttr3" id="0x0101088b"/>
+
+ Resulting public-staging.xml:
+ <!\- ===============================================================
+ Resources added in version (? + 1) of the platform
+ =============================================================== -\>
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01fd0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01fc0000">
+ </staging-public-group>
+-->
+<resources>
+
+ <!-- ===============================================================
+ Resources added in version T of the platform
+
+ NOTE: After this version of the platform is forked, changes cannot be made to the root
+ branch's groups for that release. Only merge changes to the forked platform branch.
+ =============================================================== -->
+ <eat-comment/>
+
+ <staging-public-group type="attr" first-id="0x01df0000">
+ <public name="sharedUserMaxSdkVersion" />
+ <public name="requiredSplitTypes" />
+ <public name="splitTypes" />
+ <public name="canDisplayOnRemoteDevices" />
+ <public name="supportedTypes" />
+ <public name="resetEnabledSettingsOnAppDataCleared" />
+ <public name="supportsStylusHandwriting" />
+ <public name="showClockAndComplications" />
+ <!-- @hide @SystemApi -->
+ <public name="gameSessionService" />
+ <public name="supportsBatteryGameMode" />
+ <public name="supportsPerformanceGameMode" />
+ <public name="allowGameAngleDriver" />
+ <public name="allowGameDownscaling" />
+ <public name="allowGameFpsOverride" />
+ <public name="localeConfig" />
+ <public name="showBackground" />
+ <public name="useTargetActivityForQuickAccess"/>
+ <public name="inheritKeyStoreKeys" />
+ <public name="preferKeepClear" />
+ <public name="autoHandwritingEnabled" />
+ <public name="fromExtendLeft" />
+ <public name="fromExtendTop" />
+ <public name="fromExtendRight" />
+ <public name="fromExtendBottom" />
+ <public name="toExtendLeft" />
+ <public name="toExtendTop" />
+ <public name="toExtendRight" />
+ <public name="toExtendBottom" />
+ <public name="tileService" />
+ <public name="windowSplashScreenBehavior" />
+ <public name="allowUntrustedActivityEmbedding" />
+ <public name="knownActivityEmbeddingCerts" />
+ <public name="intro" />
+ <public name="enableOnBackInvokedCallback" />
+ <public name="supportsInlineSuggestionsWithTouchExploration" />
+ <public name="lineBreakStyle" />
+ <public name="lineBreakWordStyle" />
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01de0000">
+ <public name="removed_accessibilityActionSwipeLeft" />
+ <public name="removed_accessibilityActionSwipeRight" />
+ <public name="removed_accessibilityActionSwipeUp" />
+ <public name="removed_accessibilityActionSwipeDown" />
+ <public name="accessibilityActionShowTextSuggestions" />
+ <public name="inputExtractAction" />
+ <public name="inputExtractAccessories" />
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x01dd0000">
+ <public name="TextAppearance.DeviceDefault.Headline" />
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01dc0000">
+ <!-- @hide @SystemApi -->
+ <public name="config_systemSupervision" />
+ <!-- @hide @SystemApi -->
+ <public name="config_devicePolicyManagement" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemAppProtectionService" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public name="config_systemAutomotiveCalendarSyncManager" />
+ <!-- @hide @SystemApi -->
+ <public name="config_defaultAutomotiveNavigation" />
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01db0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01da0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01d90000">
+ <!-- @hide @SystemApi -->
+ <public name="config_optionalIpSecAlgorithms" />
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01d80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01d70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01d60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01d50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01d40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01d30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01d20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01d10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01d00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01cf0000">
+ <!-- @hide @TestApi -->
+ <public name="config_preventImeStartupUnlessTextEditor" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableQrCodeScannerOnLockScreen" />
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ce0000">
+ </staging-public-group>
+
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 34bd2fbad576..602e42d2dc28 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -234,9 +234,9 @@
<!-- Displayed to tell the user that they should switch their network preference. -->
<string name="NetworkPreferenceSwitchSummary">Try changing preferred network. Tap to change.</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
- <string name="EmergencyCallWarningTitle">Emergency calling unavailable</string>
+ <string name="EmergencyCallWarningTitle">Emergency calls may be unavailable</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
- <string name="EmergencyCallWarningSummary">Can\u2019t make emergency calls over Wi\u2011Fi</string>
+ <string name="EmergencyCallWarningSummary"><xliff:g id="spn" example="Operator">%s</xliff:g> doesn\'t support emergency calls over Wi-Fi. Tap for details.</string>
<!-- Telephony notification channel name for a channel containing network alert notifications. -->
<string name="notification_channel_network_alert">Alerts</string>
@@ -4095,10 +4095,20 @@
<!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
<string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string>
- <!-- Title of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE] -->
- <string name="permlab_accessSupplementalApi">access SupplementalApis</string>
- <!-- Description of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE]-->
- <string name="permdesc_accessSupplementalApi">Allows an application to access SupplementalApis.</string>
+ <!-- Title of an application permission that lets it access AdServices Topics API. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesTopics">access AdServices Topics API</string>
+ <!-- Description of an application permission that lets it access AdServices Topics API. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesTopics">Allows an application to access AdServices Topics API.</string>
+
+ <!-- Title of an application permission that lets it access AdServices Attribution APIs. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesAttribution">access AdServices Attribution APIs</string>
+ <!-- Description of an application permission that lets it access AdServices Attribution APIs. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesAttribution">Allows an application to access AdServices Attribution APIs.</string>
+
+ <!-- Title of an application permission that lets it access AdServices Custom Audiences API. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessAdServicesCustomAudiences">access AdServices Custom Audiences API</string>
+ <!-- Description of an application permission that lets it access AdServices Custom Audiences API. [CHAR LIMIT=NONE]-->
+ <string name="permdesc_accessAdServicesCustomAudiences">Allows an application to access AdServices Custom Audiences API.</string>
<!-- Shown in the tutorial for tap twice for zoom control. -->
<string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
@@ -5721,16 +5731,17 @@
<!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
<string name="harmful_app_warning_title">Harmful app detected</string>
- <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] -->
- <string name="log_access_confirmation_title">System log access request</string>
+ <!-- Title for the log access confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="log_access_confirmation_title">Allow <xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> to access all device logs?</string>
<!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] -->
<string name="log_access_confirmation_allow">Only this time</string>
<!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
<string name="log_access_confirmation_deny">Don\u2019t allow</string>
<!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
- <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging.
- These logs might contain information that apps and services on your device have written.</string>
+ <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
+ \n\nIf you don’t allow this app to access all device logs, it can still access its own logs, and your device manufacturer may still be able to access some logs or info on your device. Learn more
+ </string>
<!-- Privacy notice do not show [CHAR LIMIT=20] -->
<string name="log_access_do_not_show_again">Don\u2019t show again</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9553f95fe5c9..7a9f520b4d3c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1499,5 +1499,43 @@ please see styles_device_defaults.xml.
<item name="textColor">#555555</item>
</style>
+ <!-- The style for log access consent text -->
+ <style name="AllowLogAccess">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:fontFamily">google-sans</item>
+ </style>
+
+ <style name="PrimaryAllowLogAccess">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:fontFamily">google-sans</item>
+ </style>
+
+ <style name="PermissionGrantButtonTop"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:layout_marginTop">2dp</item>
+ <item name="android:layout_marginBottom">2dp</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:background">@drawable/grant_permissions_buttons_top</item>
+ </style>
+
+ <style name="PermissionGrantButtonBottom"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:layout_width">332dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:layout_marginTop">2dp</item>
+ <item name="android:layout_marginBottom">2dp</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:background">@drawable/grant_permissions_buttons_bottom</item>
+ </style>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index df3cbf34c8f0..8f3abd60dd3c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3677,7 +3677,7 @@
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
- <java-symbol type="string" name="config_defaultCloudSearchService" />
+ <java-symbol type="array" name="config_defaultCloudSearchServices" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />
@@ -3883,6 +3883,10 @@
<java-symbol type="string" name="log_access_confirmation_deny" />
<java-symbol type="string" name="log_access_confirmation_title" />
<java-symbol type="string" name="log_access_confirmation_body" />
+ <java-symbol type="layout" name="log_access_user_consent_dialog_permission" />
+ <java-symbol type="id" name="log_access_dialog_title" />
+ <java-symbol type="id" name="log_access_dialog_allow_button" />
+ <java-symbol type="id" name="log_access_dialog_deny_button" />
<java-symbol type="string" name="config_defaultAssistantAccessComponent" />
diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb
deleted file mode 100644
index 373b8e4bd01f..000000000000
--- a/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb
deleted file mode 100644
index aa531d8b35bd..000000000000
--- a/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
new file mode 100644
index 000000000000..fa7d7214d289
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Test for verifying the behavior of {@link IpcDataCache}. This test does
+ * not use any actual binder calls - it is entirely self-contained. This test also relies
+ * on the test mode of {@link IpcDataCache} because Android SELinux rules do
+ * not grant test processes the permission to set system properties.
+ * <p>
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:IpcDataCacheTest
+ */
+@SmallTest
+public class IpcDataCacheTest {
+
+ // Configuration for creating caches
+ private static final String MODULE = IpcDataCache.MODULE_TEST;
+ private static final String API = "testApi";
+
+ // This class is a proxy for binder calls. It contains a counter that increments
+ // every time the class is queried.
+ private static class ServerProxy {
+ // The number of times this class was queried.
+ private int mCount = 0;
+
+ // A single query. The key behavior is that the query count is incremented.
+ boolean query(int x) {
+ mCount++;
+ return value(x);
+ }
+
+ // Return the expected value of an input, without incrementing the query count.
+ boolean value(int x) {
+ return x % 3 == 0;
+ }
+
+ // Verify the count.
+ void verify(int x) {
+ assertEquals(x, mCount);
+ }
+ }
+
+ // The functions for querying the server.
+ private static class ServerQuery
+ extends IpcDataCache.QueryHandler<Integer, Boolean> {
+ private final ServerProxy mServer;
+
+ ServerQuery(ServerProxy server) {
+ mServer = server;
+ }
+
+ @Override
+ public Boolean apply(Integer x) {
+ return mServer.query(x);
+ }
+ @Override
+ public boolean shouldBypassCache(Integer x) {
+ return x % 13 == 0;
+ }
+ }
+
+ // Clear the test mode after every test, in case this process is used for other
+ // tests. This also resets the test property map.
+ @After
+ public void tearDown() throws Exception {
+ IpcDataCache.setTestMode(false);
+ }
+
+ // This test is disabled pending an sepolicy change that allows any app to set the
+ // test property.
+ @Test
+ public void testBasicCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Create a cache that uses simple arithmetic to computer its values.
+ IpcDataCache<Integer, Boolean> testCache =
+ new IpcDataCache<>(4, MODULE, API, "testCache1",
+ new ServerQuery(tester));
+
+ IpcDataCache.setTestMode(true);
+ testCache.testPropertyName();
+
+ tester.verify(0);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(1);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(2);
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(3);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(4);
+
+ // Invalidate the cache, and verify that the next read on 3 goes to the server.
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(5);
+
+ // Test bypass. The query for 13 always bypasses the cache.
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(8);
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(9);
+ }
+
+ @Test
+ public void testDisableCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Three caches, all using the same system property but one uses a different name.
+ IpcDataCache<Integer, Boolean> cache1 =
+ new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ IpcDataCache<Integer, Boolean> cache2 =
+ new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ IpcDataCache<Integer, Boolean> cache3 =
+ new IpcDataCache<>(4, MODULE, API, "cacheB",
+ new ServerQuery(tester));
+
+ // Caches are enabled upon creation.
+ assertEquals(false, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable the cache1 instance. Only cache1 is disabled
+ cache1.disableInstance();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable cache1. This will disable cache1 and cache2 because they share the
+ // same name. cache3 has a different name and will not be disabled.
+ cache1.disableForCurrentProcess();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(true, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Create a new cache1. Verify that the new instance is disabled.
+ cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ assertEquals(true, cache1.getDisabledState());
+
+ // Remove the record of caches being locally disabled. This is a clean-up step.
+ cache1.forgetDisableLocal();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(true, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Create a new cache1. Verify that the new instance is not disabled.
+ cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
+ new ServerQuery(tester));
+ assertEquals(false, cache1.getDisabledState());
+ }
+
+ private static class TestQuery
+ extends IpcDataCache.QueryHandler<Integer, String> {
+
+ private int mRecomputeCount = 0;
+
+ @Override
+ public String apply(Integer qv) {
+ mRecomputeCount += 1;
+ return "foo" + qv.toString();
+ }
+
+ int getRecomputeCount() {
+ return mRecomputeCount;
+ }
+ }
+
+ private static class TestCache extends IpcDataCache<Integer, String> {
+ private final TestQuery mQuery;
+
+ TestCache() {
+ this(MODULE, API);
+ }
+
+ TestCache(String module, String api) {
+ this(module, api, new TestQuery());
+ }
+
+ TestCache(String module, String api, TestQuery query) {
+ super(4, module, api, "testCache7", query);
+ mQuery = query;
+ setTestMode(true);
+ testPropertyName();
+ }
+
+ int getRecomputeCount() {
+ return mQuery.getRecomputeCount();
+ }
+ }
+
+ @Test
+ public void testCacheRecompute() {
+ TestCache cache = new TestCache();
+ cache.invalidateCache();
+ assertEquals(cache.isDisabled(), false);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo6", cache.query(6));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ // Invalidate the cache with a direct call to the property.
+ IpcDataCache.invalidateCache(MODULE, API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(4, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCacheInitialState() {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCachePropertyUnset() {
+ final String UNSET_API = "otherApi";
+ TestCache cache = new TestCache(MODULE, UNSET_API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testCacheDisableState() {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ cache.disableSystemWide();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(5, cache.getRecomputeCount());
+ cache.invalidateCache(); // Should not reenable
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(7, cache.getRecomputeCount());
+ }
+
+ @Test
+ public void testLocalProcessDisable() {
+ TestCache cache = new TestCache();
+ assertEquals(cache.isDisabled(), false);
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals(cache.isDisabled(), false);
+ cache.disableForCurrentProcess();
+ assertEquals(cache.isDisabled(), true);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index e6660f32f817..d01d29bda3d6 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -55,11 +55,7 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
protected static String OBB_FILE_1_CONTENTS_1 = "OneToOneThousandInts.bin";
protected static String OBB_FILE_2 = "obb_file2.obb";
protected static String OBB_FILE_3 = "obb_file3.obb";
- protected static String OBB_FILE_1_PASSWORD = "password1";
- protected static String OBB_FILE_1_ENCRYPTED = "obb_enc_file100_orig1.obb";
protected static String OBB_FILE_2_UNSIGNED = "obb_file2_nosign.obb";
- protected static String OBB_FILE_3_PASSWORD = "password3";
- protected static String OBB_FILE_3_ENCRYPTED = "obb_enc_file100_orig3.obb";
protected static String OBB_FILE_3_BAD_PACKAGENAME = "obb_file3_bad_packagename.obb";
protected static boolean FORCE = true;
@@ -227,22 +223,21 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
* Mounts an OBB file
*
* @param obbFilePath The full path to the OBB file to mount
- * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
* @param expectedState The expected state resulting from trying to mount the OBB
* @return A {@link String} representing the normalized path to OBB file that was mounted
*/
- protected String mountObb(String obbFilePath, String key, int expectedState) {
- return doMountObb(obbFilePath, key, expectedState);
+ protected String mountObb(String obbFilePath, int expectedState) {
+ return doMountObb(obbFilePath, expectedState);
}
/**
- * Mounts an OBB file with default options (no encryption, mounting succeeds)
+ * Mounts an OBB file with default options.
*
* @param obbFilePath The full path to the OBB file to mount
* @return A {@link String} representing the normalized path to OBB file that was mounted
*/
protected String mountObb(String obbFilePath) {
- return doMountObb(obbFilePath, null, OnObbStateChangeListener.MOUNTED);
+ return doMountObb(obbFilePath, OnObbStateChangeListener.MOUNTED);
}
/**
@@ -279,13 +274,13 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
* @return true if the listener was signaled of a state change by the system; else a fail()
* is triggered if we timed out
*/
- protected String doMountObb_noThrow(String obbFilePath, String key, int expectedState) {
- Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
+ protected String doMountObb_noThrow(String obbFilePath, int expectedState) {
+ Log.i(LOG_TAG, "doMountObb() on " + obbFilePath);
assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
ObbListener obbListener = new ObbListener();
- boolean success = mSm.mountObb(obbFilePath, key, obbListener);
+ boolean success = mSm.mountObb(obbFilePath, null, obbListener);
success &= obbFilePath.equals(doWaitForObbStateChange(obbListener));
success &= (expectedState == obbListener.state());
@@ -307,17 +302,16 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
* Mounts an OBB file without throwing and synchronously waits for it to finish mounting
*
* @param obbFilePath The full path to the OBB file to mount
- * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
* @param expectedState The expected state resulting from trying to mount the OBB
* @return A {@link String} representing the actual normalized path to OBB file that was
* mounted, or null if the mounting failed
*/
- protected String doMountObb(String obbFilePath, String key, int expectedState) {
- Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
+ protected String doMountObb(String obbFilePath, int expectedState) {
+ Log.i(LOG_TAG, "doMountObb() on " + obbFilePath);
assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
ObbListener obbListener = new ObbListener();
- assertTrue("mountObb call failed", mSm.mountObb(obbFilePath, key, obbListener));
+ assertTrue("mountObb call failed", mSm.mountObb(obbFilePath, null, obbListener));
assertTrue("Failed to get OBB mount status change for file: " + obbFilePath,
doWaitForObbStateChange(obbListener));
assertEquals("OBB mount state not what was expected!", expectedState,
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 62f2ac28a601..ecd2f76a5160 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -83,58 +83,6 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
}
/**
- * Tests mounting a single encrypted OBB file and verifies its contents.
- */
- @LargeTest
- public void testMountSingleEncryptedObb() throws Exception {
- final File file = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
- String filePath = file.getAbsolutePath();
- mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
- verifyObb3Contents(filePath);
- unmountObb(filePath, DONT_FORCE);
- }
-
- /**
- * Tests mounting a single encrypted OBB file using an invalid password.
- */
- @LargeTest
- public void testMountSingleEncryptedObbInvalidPassword() throws Exception {
- final File file = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3);
- String filePath = file.getAbsolutePath();
- mountObb(filePath, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
- }
-
- /**
- * Tests simultaneously mounting 2 encrypted OBBs with different keys and verifies contents.
- */
- @LargeTest
- public void testMountTwoEncryptedObb() throws Exception {
- File file3 = null;
- File file1 = null;
- try {
- file3 = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
- String filePath3 = file3.getAbsolutePath();
- mountObb(filePath3, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
- verifyObb3Contents(filePath3);
-
- file1 = createObbFile(OBB_FILE_1_ENCRYPTED, R.raw.obb_enc_file100_orig1);
- String filePath1 = file1.getAbsolutePath();
- mountObb(filePath1, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.MOUNTED);
- verifyObb1Contents(filePath1);
-
- unmountObb(filePath3, DONT_FORCE);
- unmountObb(filePath1, DONT_FORCE);
- } finally {
- if (file3 != null) {
- file3.delete();
- }
- if (file1 != null) {
- file1.delete();
- }
- }
- }
-
- /**
* Tests mounting a single OBB that isn't signed.
*/
@LargeTest
@@ -142,7 +90,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
final File file = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign);
String filePath = file.getAbsolutePath();
try {
- mountObb(filePath, OBB_FILE_2_UNSIGNED, OnObbStateChangeListener.ERROR_INTERNAL);
+ mountObb(filePath, OnObbStateChangeListener.ERROR_INTERNAL);
fail("mountObb should've failed with an exception");
} catch (IllegalArgumentException e) {
// Expected
@@ -156,8 +104,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
public void testMountBadPackageNameObb() throws Exception {
final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
String filePath = file.getAbsolutePath();
- mountObb(filePath, OBB_FILE_3_BAD_PACKAGENAME,
- OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
}
/**
@@ -169,7 +116,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
String filePath = file.getAbsolutePath();
mountObb(filePath);
verifyObb1Contents(filePath);
- mountObb(filePath, null, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+ mountObb(filePath, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
verifyObb1Contents(filePath);
unmountObb(filePath, DONT_FORCE);
}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 78a8f7b3f32e..c4c983d24af9 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -214,4 +215,27 @@ public class MotionEventTest {
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3c64cf55eb50..deffa3a6fea6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -237,27 +237,6 @@ applications that come with the platform
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
</privapp-permissions>
- <privapp-permissions package="com.android.providers.media.module">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.USE_RESERVED_DISK"/>
- <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
- <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <permission name="android.permission.WATCH_APPOPS"/>
- <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
- <permission name="android.permission.UPDATE_DEVICE_STATS"/>
- <!-- Permissions required for reading and logging compat changes -->
- <permission name="android.permission.LOG_COMPAT_CHANGE" />
- <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
- <permission name="android.permission.REGISTER_STATS_PULL_ATOM" />
- <!-- Permissions required for reading DeviceConfig -->
- <permission name="android.permission.READ_DEVICE_CONFIG" />
- <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
- <permission name="android.permission.MODIFY_QUIET_MODE"/>
- <!-- Permissions required to check if an app is in the foreground or not during IO -->
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.providers.telephony">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 2ff888b06dd8..6abe34b1d675 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -19,12 +19,228 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.NonNull;
+import android.view.Window;
import libcore.util.NativeAllocationRegistry;
/**
- * Shader that calculates per-pixel color via a user defined Android Graphics Shading Language
- * (AGSL) function.
+ * <p>A {@link RuntimeShader} calculates a per-pixel color based on the output of a user defined
+ * Android Graphics Shading Language (AGSL) function.</p>
+ *
+ * <h3>Android Graphics Shading Language</h3>
+ * <p>The AGSL syntax is very similar to OpenGL ES Shading Language, but there are some important
+ * differences that are highlighted here. Most of these differences are summed up in one basic fact:
+ * <b>With GPU shading languages, you are programming a stage of the GPU pipeline. With AGSL, you
+ * are programming a stage of the {@link Canvas} or {@link RenderNode} drawing pipeline.</b></p>
+ *
+ * <p>In particular, a GLSL fragment shader controls the entire behavior of the GPU between the
+ * rasterizer and the blending hardware. That shader does all of the work to compute a color, and
+ * the color it generates is exactly what is fed to the blending stage of the pipeline.</p>
+ *
+ * <p>In contrast, AGSL functions exist as part of a larger pipeline. When you issue a
+ * {@link Canvas} drawing operation, Android (generally) assembles a single GPU fragment shader to
+ * do all of the required work. This shader typically includes several pieces. For example, it might
+ * include:</p>
+ * <ul>
+ * <li>Evaluating whether a pixel falls inside or outside of the shape being drawn (or on the
+ * border, where it might apply antialiasing).</li>
+ * <li>Evaluating whether a pixel falls inside or outside of the clipping region (again, with
+ * possible antialiasing logic for border pixels).</li>
+ * <li>Logic for the {@link Shader}, {@link ColorFilter}, and {@link BlendMode} on the
+ * {@link Paint}.</li>
+ * <li>Color space conversion code, as part of Android’s color management.</li>
+ * </ul>
+ *
+ * <p>A {@link RuntimeShader}, like other {@link Shader} types, effectively contributes a function
+ * to the GPU’s fragment shader.</p>
+ *
+ * <h3>AGSL Shader Execution</h3>
+ * <p>Just like a GLSL shader, an AGSL shader begins execution in a main function. Unlike GLSL, the
+ * function receives as an input parameter the position of the pixel within the {@link Canvas} or
+ * {@link RenderNode} coordinate space (similar to gl_fragCoord) and returns the color to be shaded
+ * as a vec4 (similar to out vec4 color or gl_FragColor in GLSL).</p>
+ *
+ * <pre class="prettyprint">
+ * vec4 main(vec2 canvas_coordinates);
+ * </pre>
+ *
+ * <p>AGSL and GLSL use different coordinate spaces by default. In GLSL, the fragment coordinate
+ * (fragCoord) is relative to the lower left. AGSL matches the screen coordinate system of the
+ * Android {@link Canvas} which has its origin as the upper left corner. This means that the
+ * coordinates provided as a parameter in the main function are local to the canvas with the
+ * exception of any {@link Shader#getLocalMatrix(Matrix)} transformations applied to this shader.
+ * Additionally, if the shader is invoked by another using {@link #setInputShader(String, Shader)},
+ * then that parent shader may modify the input coordinates arbitrarily.</p>
+ *
+ * <h3>AGSL and Color Spaces</h3>
+ * <p>Android Graphics and by extension {@link RuntimeShader} are color managed. The working
+ * {@link ColorSpace} for an AGSL shader is defined to be the color space of the destination, which
+ * in most cases is determined by {@link Window#setColorMode(int)}.</p>
+ *
+ * <p>When authoring an AGSL shader, you won’t know what the working color space is. For many
+ * effects, this is fine because by default color inputs are automatically converted into the
+ * working color space. For certain effects, it may be important to do some math in a fixed, known
+ * color space. A common example is lighting – to get physically accurate lighting, math should be
+ * done in a linear color space. To help with this, AGSL provides two intrinsic functions that
+ * convert colors between the working color space and the
+ * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB} color space:
+ *
+ * <pre class="prettyprint">
+ * vec3 toLinearSrgb(vec3 color);
+ * vec3 fromLinearSrgb(vec3 color);</pre>
+ *
+ * <h3>AGSL and Premultiplied Alpha</h3>
+ * <p>When dealing with transparent colors, there are two (common) possible representations:
+ * straight (unassociated) alpha and premultiplied (associated) alpha. In ASGL the color returned
+ * by the main function is expected to be premultiplied. AGSL’s use of premultiplied alpha
+ * implies:
+ * </p>
+ *
+ * <ul>
+ * <li>If your AGSL shader will return transparent colors, be sure to multiply the RGB by A. The
+ * resulting color should be [R*A, G*A, B*A, A], not [R, G, B, A].</li>
+ * <li>For more complex shaders, you must understand which of your colors are premultiplied vs.
+ * straight. Many operations don’t make sense if you mix both kinds of color together.</li>
+ * </ul>
+ *
+ * <h3>Uniforms</h3>
+ * <p>AGSL, like GLSL, exposes the concept of uniforms. An AGSL uniform is defined as a read-only,
+ * global variable that is accessible by the AGSL code and is initialized by a number of setter
+ * methods on {@link RuntimeShader}. AGSL exposes two primitive uniform data types (float, int) and
+ * two specialized types (colors, shaders) that are outlined below.</p>
+ *
+ * <h4>Primitive Uniforms</h4>
+ * <p>There are two primitive uniform types supported by AGSL, float and int. For these types and
+ * uniforms representing a grouping of these types, like arrays and matrices, there are
+ * corresponding {@link RuntimeShader} methods to initialize them.
+ * <table border="2" width="85%" align="center" cellpadding="5">
+ * <thead>
+ * <tr><th>Java Type</th> <th>AGSL Type</th> <th>Method</th> </tr>
+ * </thead>
+ *
+ * <tbody>
+ * <tr>
+ * <td rowspan="4">Floats</td>
+ * <td>float</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec2</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec3</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td>vec4</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float, float, float, float)}</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="4">Integers</td>
+ * <td>int</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec2</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec3</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td>ivec4</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int, int, int, int)}</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="2">Matrices and Arrays</td>
+ * <td>mat2, mat3, and mat4, and float[]</td>
+ * <td>{@link RuntimeShader#setFloatUniform(String, float[])}</td>
+ * </tr>
+ * <tr>
+ * <td>int[]</td>
+ * <td>{@link RuntimeShader#setIntUniform(String, int[])}</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * For example, a simple AGSL shader making use of a float uniform to modulate the transparency
+ * of the output color would look like:</p>
+ *
+ * <pre class="prettyprint">
+ * uniform float alpha;
+ * vec4 main(vec2 canvas_coordinates) {
+ * vec3 red = vec3(1.0, 0.0, 0.0);
+ * return vec4(red * alpha, alpha);
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the uniform can then be initialized
+ * and updated per frame by calling {@link RuntimeShader#setFloatUniform(String, float)} with the
+ * value of alpha. The value of a primitive uniform defaults to 0 if it is declared in the AGSL
+ * shader but not initialized.</p>
+ *
+ * <h4>Color Uniforms</h4>
+ * <p>AGSL doesn't know if uniform variables contain colors, it won't automatically convert them to
+ * the working colorspace of the shader at runtime. However, you can label your vec4 uniform with
+ * the "layout(color)" qualifier which lets Android know that the uniform will be used as a color.
+ * Doing so allows AGSL to transform the uniform value to the working color space. In AGSL, declare
+ * the uniform like this:
+ *
+ * <pre class="prettyprint">
+ * layout(color) uniform vec4 inputColorA;
+ * layout(color) uniform vec4 inputColorB;
+ * vec4 main(vec2 canvas_coordinates) {
+ * // blend the two colors together and return the resulting color
+ * return mix(inputColorA, inputColorB, 0.5);
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the uniforms can
+ * then be initialized and updated per frame by calling
+ * {@link RuntimeShader#setColorUniform(String, int)},
+ * {@link RuntimeShader#setColorUniform(String, long)}, or
+ * {@link RuntimeShader#setColorUniform(String, Color)} with the desired colors. The value of a
+ * color uniform is undefined if it is declared in the AGSL shader but not initialized.</p>
+ *
+ * <h4>Shader Uniforms</h4>
+ * In GLSL, a fragment shader can sample a texture. For AGSL instead of sampling textures you can
+ * sample from any {@link Shader}, which includes but is not limited to {@link BitmapShader}. To
+ * make it clear that you are operating on an {@link Shader} object there is no "sample"
+ * method. Instead, the shader uniform has an "eval()" method. This distinction enables AGSL shaders
+ * to sample from existing bitmap and gradient shaders as well as other {@link RuntimeShader}
+ * objects. In AGSL, declare the uniform like this:
+ *
+ * <pre class="prettyprint">
+ * uniform shader myShader;
+ * vec4 main(vec2 canvas_coordinates) {
+ * // swap the red and blue color channels when sampling from myShader
+ * return myShader.sample(canvas_coordinates).bgra;
+ * }</pre>
+ *
+ * <p>After creating a {@link RuntimeShader} with that program the shader uniform can
+ * then be initialized and updated per frame by calling
+ * {@link RuntimeShader#setInputShader(String, Shader)} with the desired shader. The value of a
+ * shader uniform is undefined if it is declared in the AGSL shader but not initialized.</p>
+ *
+ * <p>Although most {@link BitmapShader}s contain colors that should be color managed, some contain
+ * data that isn’t actually colors. This includes bitmaps storing normals, material properties
+ * (e.g. roughness), heightmaps, or any other purely mathematical data that happens to be stored in
+ * a bitmap. When using these kinds of shaders in AGSL, you probably want to initialize them with
+ * {@link #setInputBuffer(String, BitmapShader)}. Shaders initialized this way work much like
+ * a regular {@link BitmapShader} (including filtering and tiling), with a few major differences:
+ * <ul>
+ * <li>No color space transformation is applied (the color space of the bitmap is ignored).</li>
+ * <li>Bitmaps that return false for {@link Bitmap#isPremultiplied()} are not automatically
+ * premultiplied.</li>
+ * </ul>
+ *
+ * <p>In addition, when sampling from a {@link BitmapShader} be aware that the shader does not use
+ * normalized coordinates (like a texture in GLSL). It uses (0, 0) in the upper-left corner, and
+ * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you’re
+ * evaluating the shader with coordinates based on the ones passed to your AGSL program, the scale
+ * is correct. However, if you want to adjust those coordinates (to do some kind of re-mapping of
+ * the bitmap), remember that the coordinates are local to the canvas.</p>
+ *
*/
public class RuntimeShader extends Shader {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 358104fffbf6..e5d127609b2e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -83,16 +83,10 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
- put("KeyPairGenerator." + X25519_ALIAS,
- PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
- put("KeyPairGenerator." + ED25519_OID,
- PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// java.security.KeyFactory
putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA");
- putKeyFactoryImpl(X25519_ALIAS);
- putKeyFactoryImpl(ED25519_OID);
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index eb9429747b66..1c49881904e4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -18,17 +18,73 @@ package androidx.window.common;
import static androidx.window.util.ExtensionHelper.isZero;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.util.Log;
import androidx.annotation.NonNull;
+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.regex.Matcher;
import java.util.regex.Pattern;
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-final class CommonDisplayFeature implements DisplayFeature {
+/** A representation of a folding feature for both Extension and Sidecar.
+ * For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
+ * {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
+ * {@link androidx.window.extensions.layout.FoldingFeature}.
+ */
+public final class CommonFoldingFeature {
+
+ private static final boolean DEBUG = false;
+
+ public static final String TAG = CommonFoldingFeature.class.getSimpleName();
+
+ /**
+ * A common type to represent a hinge where the screen is continuous.
+ */
+ public static final int COMMON_TYPE_FOLD = 1;
+
+ /**
+ * A common type to represent a hinge where there is a physical gap separating multiple
+ * displays.
+ */
+ public static final int COMMON_TYPE_HINGE = 2;
+
+ @IntDef({COMMON_TYPE_FOLD, COMMON_TYPE_HINGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {
+ }
+
+ /**
+ * A common state to represent when the state is not known. One example is if the device is
+ * closed. We do not emit this value for developers but is useful for implementation reasons.
+ */
+ public static final int COMMON_STATE_UNKNOWN = -1;
+
+ /**
+ * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+ * and Extensions do not match exactly.
+ */
+ public static final int COMMON_STATE_FLAT = 3;
+ /**
+ * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
+ * Sidecar and Extensions do not match exactly.
+ */
+ public static final int COMMON_STATE_HALF_OPENED = 2;
+
+ /**
+ * The possible states for a folding hinge.
+ */
+ @IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
private static final Pattern FEATURE_PATTERN =
Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?");
@@ -38,17 +94,49 @@ final class CommonDisplayFeature implements DisplayFeature {
private static final String PATTERN_STATE_FLAT = "flat";
private static final String PATTERN_STATE_HALF_OPENED = "half-opened";
- // TODO(b/183049815): Support feature strings that include the state of the feature.
+ /**
+ * Parse a {@link List} of {@link CommonFoldingFeature} from a {@link String}.
+ * @param value a {@link String} representation of multiple {@link CommonFoldingFeature}
+ * separated by a ":".
+ * @param hingeState a global fallback value for a {@link CommonFoldingFeature} if one is not
+ * specified in the input.
+ * @throws IllegalArgumentException if the provided string is improperly formatted or could not
+ * otherwise be parsed.
+ * @see #FEATURE_PATTERN
+ * @return {@link List} of {@link CommonFoldingFeature}.
+ */
+ static List<CommonFoldingFeature> parseListFromString(@NonNull String value,
+ @State int hingeState) {
+ List<CommonFoldingFeature> features = new ArrayList<>();
+ String[] featureStrings = value.split(";");
+ for (String featureString : featureStrings) {
+ CommonFoldingFeature feature;
+ try {
+ feature = CommonFoldingFeature.parseFromString(featureString, hingeState);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+ }
+ continue;
+ }
+ features.add(feature);
+ }
+ return features;
+ }
/**
* Parses a display feature from a string.
*
+ * @param string A {@link String} representation of a {@link CommonFoldingFeature}.
+ * @param hingeState A fallback value for the {@link State} if it is not specified in the input.
* @throws IllegalArgumentException if the provided string is improperly formatted or could not
* otherwise be parsed.
+ * @return {@link CommonFoldingFeature} represented by the {@link String} value.
* @see #FEATURE_PATTERN
*/
@NonNull
- static CommonDisplayFeature parseFromString(@NonNull String string) {
+ private static CommonFoldingFeature parseFromString(@NonNull String string,
+ @State int hingeState) {
Matcher featureMatcher = FEATURE_PATTERN.matcher(string);
if (!featureMatcher.matches()) {
throw new IllegalArgumentException("Malformed feature description format: " + string);
@@ -59,10 +147,10 @@ final class CommonDisplayFeature implements DisplayFeature {
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
- type = 1 /* TYPE_FOLD */;
+ type = COMMON_TYPE_FOLD;
break;
case FEATURE_TYPE_HINGE:
- type = 2 /* TYPE_HINGE */;
+ type = COMMON_TYPE_HINGE;
break;
default: {
throw new IllegalArgumentException("Malformed feature type: " + featureType);
@@ -79,7 +167,7 @@ final class CommonDisplayFeature implements DisplayFeature {
}
String stateString = featureMatcher.group(6);
stateString = stateString == null ? "" : stateString;
- Integer state;
+ final int state;
switch (stateString) {
case PATTERN_STATE_FLAT:
state = COMMON_STATE_FLAT;
@@ -88,10 +176,10 @@ final class CommonDisplayFeature implements DisplayFeature {
state = COMMON_STATE_HALF_OPENED;
break;
default:
- state = null;
+ state = hingeState;
break;
}
- return new CommonDisplayFeature(type, state, featureRect);
+ return new CommonFoldingFeature(type, state, featureRect);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed feature description: " + string, e);
}
@@ -99,11 +187,11 @@ final class CommonDisplayFeature implements DisplayFeature {
private final int mType;
@Nullable
- private final Integer mState;
+ private final int mState;
@NonNull
private final Rect mRect;
- CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+ CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
assertValidState(state);
this.mType = type;
this.mState = state;
@@ -114,16 +202,19 @@ final class CommonDisplayFeature implements DisplayFeature {
this.mRect = rect;
}
+ /** Returns the type of the feature. */
+ @Type
public int getType() {
return mType;
}
- /** Returns the state of the feature, or {@code null} if the feature has no state. */
- @Nullable
- public Integer getState() {
+ /** Returns the state of the feature.*/
+ @State
+ public int getState() {
return mState;
}
+ /** Returns the bounds of the feature. */
@NonNull
public Rect getRect() {
return mRect;
@@ -133,7 +224,7 @@ final class CommonDisplayFeature implements DisplayFeature {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- CommonDisplayFeature that = (CommonDisplayFeature) o;
+ CommonFoldingFeature that = (CommonFoldingFeature) o;
return mType == that.mType
&& Objects.equals(mState, that.mState)
&& mRect.equals(that.mRect);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index fa9a5a8b7a1b..6987401525b4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -18,11 +18,15 @@ package androidx.window.common;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.parseListFromString;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
@@ -30,6 +34,7 @@ import androidx.window.util.BaseDataProducer;
import com.android.internal.R;
+import java.util.List;
import java.util.Optional;
/**
@@ -37,10 +42,13 @@ import java.util.Optional;
* by mapping the state returned from {@link DeviceStateManager} to values provided in the resources
* config at {@link R.array#config_device_state_postures}.
*/
-public final class DeviceStateManagerPostureProducer extends BaseDataProducer<Integer> {
- private static final String TAG = "ConfigDevicePostureProducer";
+public final class DeviceStateManagerFoldingFeatureProducer extends
+ BaseDataProducer<List<CommonFoldingFeature>> {
+ private static final String TAG =
+ DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false;
+ private final Context mContext;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
@@ -50,7 +58,8 @@ public final class DeviceStateManagerPostureProducer extends BaseDataProducer<In
notifyDataChanged();
};
- public DeviceStateManagerPostureProducer(@NonNull Context context) {
+ public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) {
+ mContext = context;
String[] deviceStatePosturePairs = context.getResources()
.getStringArray(R.array.config_device_state_postures);
for (String deviceStatePosturePair : deviceStatePosturePairs) {
@@ -86,8 +95,17 @@ public final class DeviceStateManagerPostureProducer extends BaseDataProducer<In
@Override
@Nullable
- public Optional<Integer> getData() {
- final int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, -1);
- return posture != -1 ? Optional.of(posture) : Optional.empty();
+ public Optional<List<CommonFoldingFeature>> getData() {
+ final int globalHingeState = globalHingeState();
+ String displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return Optional.empty();
+ }
+ return Optional.of(parseListFromString(displayFeaturesString, globalHingeState));
+ }
+
+ private int globalHingeState() {
+ return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
deleted file mode 100644
index 573641857b99..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.common;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-
-import androidx.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-public interface DisplayFeature {
- /** Returns the type of the feature. */
- int getType();
-
- /** Returns the state of the feature, or {@code null} if the feature has no state. */
- @Nullable
- @State
- Integer getState();
-
- /** Returns the bounds of the feature. */
- @NonNull
- Rect getRect();
-
- /**
- * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
- * and Extensions do not match exactly.
- */
- int COMMON_STATE_FLAT = 3;
- /**
- * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
- * Sidecar and Extensions do not match exactly.
- */
- int COMMON_STATE_HALF_OPENED = 2;
-
- /**
- * The possible states for a folding hinge.
- */
- @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
- @Retention(RetentionPolicy.SOURCE)
- @interface State {}
-
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java
new file mode 100644
index 000000000000..d923a46c3b5d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.common;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+/**
+ * An empty implementation of {@link Application.ActivityLifecycleCallbacks} derived classes can
+ * implement the methods necessary.
+ */
+public class EmptyLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
deleted file mode 100644
index cd2cadc082e1..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.common;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.window.util.BaseDataProducer;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Implementation of {@link androidx.window.util.DataProducer} that produces
- * {@link CommonDisplayFeature} parsed from a string stored in the resources config at
- * {@link R.string#config_display_features}.
- */
-public final class ResourceConfigDisplayFeatureProducer extends
- BaseDataProducer<List<DisplayFeature>> {
- private static final boolean DEBUG = false;
- private static final String TAG = "ResourceConfigDisplayFeatureProducer";
-
- private final Context mContext;
-
- public ResourceConfigDisplayFeatureProducer(@NonNull Context context) {
- mContext = context;
- }
-
- @Override
- @Nullable
- public Optional<List<DisplayFeature>> getData() {
- String displayFeaturesString = mContext.getResources().getString(
- R.string.config_display_features);
- if (TextUtils.isEmpty(displayFeaturesString)) {
- return Optional.empty();
- }
-
- List<DisplayFeature> features = new ArrayList<>();
- String[] featureStrings = displayFeaturesString.split(";");
- for (String featureString : featureStrings) {
- CommonDisplayFeature feature;
- try {
- feature = CommonDisplayFeature.parseFromString(featureString);
- } catch (IllegalArgumentException e) {
- if (DEBUG) {
- Log.w(TAG, "Failed to parse display feature: " + featureString, e);
- }
- continue;
- }
- features.add(feature);
- }
- return Optional.of(features);
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
deleted file mode 100644
index 2026df3fa979..000000000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.common;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-
-import androidx.window.util.BaseDataProducer;
-
-import java.util.Optional;
-
-/**
- * Implementation of {@link androidx.window.util.DataProducer} that provides the device posture
- * as an {@link Integer} from a value stored in {@link Settings}.
- */
-public final class SettingsDevicePostureProducer extends BaseDataProducer<Integer> {
- private static final String DEVICE_POSTURE = "device_posture";
-
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
-
- private final ContentResolver mResolver;
- private final ContentObserver mObserver;
- private boolean mRegisteredObservers;
-
- public SettingsDevicePostureProducer(@NonNull Context context) {
- mResolver = context.getContentResolver();
- mObserver = new SettingsObserver();
- }
-
- @Override
- @Nullable
- public Optional<Integer> getData() {
- int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, -1);
- return posture == -1 ? Optional.empty() : Optional.of(posture);
- }
-
- /**
- * Registers settings observers, if needed. When settings observers are registered for this
- * producer callbacks for changes in data will be triggered.
- */
- public void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
- mObserver /* ContentObserver */);
- }
-
- /**
- * Unregisters settings observers, if needed. When settings observers are unregistered for this
- * producer callbacks for changes in data will not be triggered.
- */
- public void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(mObserver);
- }
-
- private final class SettingsObserver extends ContentObserver {
- SettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (mDevicePostureUri.equals(uri)) {
- notifyDataChanged();
- }
- }
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
index 040662657a74..e9d213e06fa9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
@@ -16,8 +16,10 @@
package androidx.window.common;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.parseListFromString;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -26,22 +28,19 @@ import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Log;
import androidx.window.util.BaseDataProducer;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* Implementation of {@link androidx.window.util.DataProducer} that produces
- * {@link CommonDisplayFeature} parsed from a string stored in {@link Settings}.
+ * {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}.
*/
public final class SettingsDisplayFeatureProducer
- extends BaseDataProducer<List<DisplayFeature>> {
- private static final boolean DEBUG = false;
- private static final String TAG = "SettingsDisplayFeatureProducer";
+ extends BaseDataProducer<List<CommonFoldingFeature>> {
private static final String DISPLAY_FEATURES = "display_features";
private final Uri mDisplayFeaturesUri =
@@ -57,32 +56,17 @@ public final class SettingsDisplayFeatureProducer
}
@Override
- @Nullable
- public Optional<List<DisplayFeature>> getData() {
+ @NonNull
+ public Optional<List<CommonFoldingFeature>> getData() {
String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
if (displayFeaturesString == null) {
return Optional.empty();
}
- List<DisplayFeature> features = new ArrayList<>();
if (TextUtils.isEmpty(displayFeaturesString)) {
- return Optional.of(features);
- }
- String[] featureStrings = displayFeaturesString.split(";");
-
- for (String featureString : featureStrings) {
- CommonDisplayFeature feature;
- try {
- feature = CommonDisplayFeature.parseFromString(featureString);
- } catch (IllegalArgumentException e) {
- if (DEBUG) {
- Log.w(TAG, "Failed to parse display feature: " + featureString, e);
- }
- continue;
- }
- features.add(feature);
+ return Optional.of(Collections.emptyList());
}
- return Optional.of(features);
+ return Optional.of(parseListFromString(displayFeaturesString, COMMON_STATE_UNKNOWN));
}
/**
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 6d8383372461..1d2b9384f47d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -28,7 +28,6 @@ import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
-import android.app.Application.ActivityLifecycleCallbacks;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +40,8 @@ import android.os.Looper;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -763,11 +764,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
}
- private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
-
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- }
+ private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@Override
public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
@@ -779,30 +776,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void onActivityStarted(Activity activity) {
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- }
-
- @Override
public void onActivityConfigurationChanged(Activity activity) {
SplitController.this.onActivityConfigurationChanged(activity);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index fe9ce971d4d9..a4fbdbc493f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -18,28 +18,30 @@ package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT;
-import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.annotation.Nullable;
import android.app.Activity;
+import android.app.Application;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.common.DeviceStateManagerPostureProducer;
-import androidx.window.common.DisplayFeature;
-import androidx.window.common.ResourceConfigDisplayFeatureProducer;
-import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.CommonFoldingFeature;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -56,36 +58,27 @@ import java.util.function.Consumer;
*/
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
- private static WindowLayoutComponent sInstance;
private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
- new HashMap<>();
-
- private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
- private final DataProducer<Integer> mDevicePostureProducer;
+ new ArrayMap<>();
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
- private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
+ private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
public WindowLayoutComponentImpl(Context context) {
- mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
- mDevicePostureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDevicePostureProducer,
- new DeviceStateManagerPostureProducer(context)
- ));
-
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
- mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+ mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
mSettingsDisplayFeatureProducer,
- new ResourceConfigDisplayFeatureProducer(context)
+ new DeviceStateManagerFoldingFeatureProducer(context)
));
-
- mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
+ mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
/**
* Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
+ *
* @param activity hosting a {@link android.view.Window}
* @param consumer interested in receiving updates to {@link WindowLayoutInfo}
*/
@@ -97,6 +90,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
/**
* Removes a listener no longer interested in receiving updates.
+ *
* @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
*/
public void removeWindowLayoutInfoListener(
@@ -118,43 +112,34 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return mWindowLayoutChangeListeners.keySet();
}
- protected boolean hasListeners() {
- return !mWindowLayoutChangeListeners.isEmpty();
+ @NonNull
+ private boolean isListeningForLayoutChanges(IBinder token) {
+ for (Activity activity: getActivitiesListeningForLayoutChanges()) {
+ if (token.equals(activity.getWindow().getAttributes().token)) {
+ return true;
+ }
+ }
+ return false;
}
- /**
- * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer.
- * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned.
- * The {@link FoldingFeature} should be ignored in the case of an invalid
- * {@link DisplayFeature.State}.
- *
- * @param feature a {@link DisplayFeature} to provide the feature state if present.
- * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture
- * produce if present.
- */
- @Nullable
- private Integer getFeatureState(DisplayFeature feature) {
- Integer featureState = feature.getState();
- Optional<Integer> posture = mDevicePostureProducer.getData();
- Integer state = featureState == null ? posture.orElse(null) : featureState;
- return convertToExtensionState(state);
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListeners.isEmpty();
}
/**
* A convenience method to translate from the common feature state to the extensions feature
- * state. More specifically, translates from {@link DisplayFeature.State} to
+ * state. More specifically, translates from {@link CommonFoldingFeature.State} to
* {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
* possible to translate, then we will return a {@code null} value.
*
- * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise.
- * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if
- * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise.
+ * @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null}
+ * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or
+ * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in
+ * {@link CommonFoldingFeature.State} and {@code null} otherwise.
*/
@Nullable
- private Integer convertToExtensionState(@Nullable Integer state) {
- if (state == null) { // The null check avoids a NullPointerException.
- return null;
- } else if (state == COMMON_STATE_FLAT) {
+ private Integer convertToExtensionState(int state) {
+ if (state == COMMON_STATE_FLAT) {
return FoldingFeature.STATE_FLAT;
} else if (state == COMMON_STATE_HALF_OPENED) {
return FoldingFeature.STATE_HALF_OPENED;
@@ -172,33 +157,30 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
@NonNull
private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
- List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
- getDisplayFeatures(activity);
+ List<DisplayFeature> displayFeatures = getDisplayFeatures(activity);
return new WindowLayoutInfo(displayFeatures);
}
/**
- * Translate from the {@link DisplayFeature} to
- * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a
- * {@link DisplayFeature} is not valid then it will be omitted.
+ * Translate from the {@link CommonFoldingFeature} to
+ * {@link DisplayFeature} for a given {@link Activity}. If a
+ * {@link CommonFoldingFeature} is not valid then it will be omitted.
*
* For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window
- * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or
- * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be
- * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is
- * not valid, the {@link FoldingFeature} is omitted from the {@link List} of
- * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid,
- * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since
- * this can cause negative UI effects down stream.
+ * coordinate space and the state is calculated from {@link CommonFoldingFeature#getState()}.
+ * The state from {@link #mFoldingFeatureProducer} may not be valid since
+ * {@link #mFoldingFeatureProducer} is a general state controller. If the state is not valid,
+ * the {@link FoldingFeature} is omitted from the {@link List} of {@link DisplayFeature}. If the
+ * bounds are not valid, constructing a {@link FoldingFeature} will throw an
+ * {@link IllegalArgumentException} since this can cause negative UI effects down stream.
*
* @param activity a proxy for the {@link android.view.Window} that contains the
- * {@link androidx.window.extensions.layout.DisplayFeature}.
- * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that
+ * {@link DisplayFeature}.
+ * @return a {@link List} of valid {@link DisplayFeature} that
* are within the {@link android.view.Window} of the {@link Activity}
*/
- private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
- @NonNull Activity activity) {
- List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
+ private List<DisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<DisplayFeature> features = new ArrayList<>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -211,11 +193,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return features;
}
- Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
if (storedFeatures.isPresent()) {
-
- for (DisplayFeature baseFeature : storedFeatures.get()) {
- Integer state = getFeatureState(baseFeature);
+ for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
+ Integer state = convertToExtensionState(baseFeature.getState());
if (state == null) {
continue;
}
@@ -223,8 +204,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(activity, featureRect);
- features.add(new FoldingFeature(featureRect, baseFeature.getType(),
- getFeatureState(baseFeature)));
+ features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
}
return features;
@@ -232,13 +212,31 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private void updateRegistrations() {
if (hasListeners()) {
- mSettingsDevicePostureProducer.registerObserversIfNeeded();
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
} else {
- mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
}
-
onDisplayFeaturesChanged();
}
+
+ private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ super.onActivityCreated(activity, savedInstanceState);
+ onDisplayFeaturesChangedIfListening(activity);
+ }
+
+ @Override
+ public void onActivityConfigurationChanged(Activity activity) {
+ super.onActivityConfigurationChanged(activity);
+ onDisplayFeaturesChangedIfListening(activity);
+ }
+
+ private void onDisplayFeaturesChangedIfListening(Activity activity) {
+ IBinder token = activity.getWindow().getAttributes().token;
+ if (token == null || isListeningForLayoutChanges(token)) {
+ onDisplayFeaturesChanged();
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index aa949f126154..c7b709347060 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -23,16 +23,17 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity;
import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.common.DeviceStateManagerPostureProducer;
-import androidx.window.common.DisplayFeature;
-import androidx.window.common.ResourceConfigDisplayFeatureProducer;
-import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.CommonFoldingFeature;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
@@ -48,36 +49,25 @@ import java.util.Optional;
*/
class SampleSidecarImpl extends StubSidecar {
private static final String TAG = "SampleSidecar";
- private static final boolean DEBUG = false;
- private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
- private final DataProducer<Integer> mDevicePostureProducer;
+ private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
- private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
- private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
+ private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer;
SampleSidecarImpl(Context context) {
- mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
- mDevicePostureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDevicePostureProducer,
- new DeviceStateManagerPostureProducer(context)
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
+ mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context);
+ mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsFoldingFeatureProducer,
+ new DeviceStateManagerFoldingFeatureProducer(context)
));
- mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
- mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
- mSettingsDisplayFeatureProducer,
- new ResourceConfigDisplayFeatureProducer(context)
- ));
-
- mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
- mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- }
-
- private void onDevicePostureChanged() {
- updateDeviceState(getDeviceState());
+ mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
private void onDisplayFeaturesChanged() {
+ updateDeviceState(getDeviceState());
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
updateWindowLayout(windowToken, newLayout);
@@ -87,27 +77,21 @@ class SampleSidecarImpl extends StubSidecar {
@NonNull
@Override
public SidecarDeviceState getDeviceState() {
- Optional<Integer> posture = mDevicePostureProducer.getData();
-
SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture.orElse(deviceStateFromFeature());
+ deviceState.posture = deviceStateFromFeature();
return deviceState;
}
private int deviceStateFromFeature() {
- List<DisplayFeature> storedFeatures = mDisplayFeatureProducer.getData()
+ List<CommonFoldingFeature> storedFeatures = mFoldingFeatureProducer.getData()
.orElse(Collections.emptyList());
for (int i = 0; i < storedFeatures.size(); i++) {
- DisplayFeature feature = storedFeatures.get(i);
- final int state = feature.getState() == null ? -1 : feature.getState();
- if (DEBUG && feature.getState() == null) {
- Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature);
- }
-
+ CommonFoldingFeature feature = storedFeatures.get(i);
+ final int state = feature.getState();
switch (state) {
- case DisplayFeature.COMMON_STATE_FLAT:
+ case CommonFoldingFeature.COMMON_STATE_FLAT:
return SidecarDeviceState.POSTURE_OPENED;
- case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ case CommonFoldingFeature.COMMON_STATE_HALF_OPENED:
return SidecarDeviceState.POSTURE_HALF_OPENED;
}
}
@@ -127,22 +111,22 @@ class SampleSidecarImpl extends StubSidecar {
}
private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
- List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
- return features;
+ return Collections.emptyList();
}
if (activity.isInMultiWindowMode()) {
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
- return features;
+ return Collections.emptyList();
}
- Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
+ List<SidecarDisplayFeature> features = new ArrayList<>();
if (storedFeatures.isPresent()) {
- for (DisplayFeature baseFeature : storedFeatures.get()) {
+ for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
SidecarDisplayFeature feature = new SidecarDisplayFeature();
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
@@ -152,17 +136,37 @@ class SampleSidecarImpl extends StubSidecar {
features.add(feature);
}
}
- return features;
+ return Collections.unmodifiableList(features);
}
@Override
protected void onListenersChanged() {
if (hasListeners()) {
- mSettingsDevicePostureProducer.registerObserversIfNeeded();
- mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
+ mSettingsFoldingFeatureProducer.registerObserversIfNeeded();
+ onDisplayFeaturesChanged();
} else {
- mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
- mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
+ mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded();
+ }
+ }
+
+ private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ super.onActivityCreated(activity, savedInstanceState);
+ onDisplayFeaturesChangedForActivity(activity);
+ }
+
+ @Override
+ public void onActivityConfigurationChanged(Activity activity) {
+ super.onActivityConfigurationChanged(activity);
+ onDisplayFeaturesChangedForActivity(activity);
+ }
+
+ private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) {
+ IBinder token = activity.getWindow().getAttributes().token;
+ if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) {
+ onDisplayFeaturesChanged();
+ }
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
index 199c37315c07..b9c808a6569b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
@@ -30,7 +30,7 @@ import java.util.Set;
abstract class StubSidecar implements SidecarInterface {
private SidecarCallback mSidecarCallback;
- private final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
+ final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
private boolean mDeviceStateChangeListenerRegistered;
StubSidecar() {
diff --git a/packages/SystemUI/res/layout/communal_host_view.xml b/libs/WindowManager/Shell/res/color/letterbox_education_dismiss_button_background_ripple.xml
index cd9c26065dc1..43cba1a37bc8 100644
--- a/packages/SystemUI/res/layout/communal_host_view.xml
+++ b/libs/WindowManager/Shell/res/color/letterbox_education_dismiss_button_background_ripple.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 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,10 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.systemui.communal.CommunalHostView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/communal_host"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/> \ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_900" android:alpha="0.6" />
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
index 0d8a8faa9798..42572d64b96f 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@android:color/system_accent1_10">
+ android:color="@color/letterbox_education_dismiss_button_background_ripple">
<item android:drawable="@drawable/letterbox_education_dismiss_button_background"/>
</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index c7b8a130e141..09ed9b8e52ee 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -39,5 +39,10 @@
<!-- Button to collapse/shrink the picture-in-picture (PIP) window [CHAR LIMIT=30] -->
<string name="pip_collapse">Collapse PIP</string>
+
+ <!-- Educative text instructing the user to double press the HOME button to access the pip
+ controls menu [CHAR LIMIT=50] -->
+ <string name="pip_edu_text"> Double press <annotation icon="home_icon"> HOME </annotation> for
+ controls </string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 91ea436f81b6..3f8343a7abd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -98,16 +98,22 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
default void onTaskVanished(RunningTaskInfo taskInfo) {}
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
- /** Whether this task listener supports compat UI. */
+ /** Whether this task listener supports compat UI. */
default boolean supportCompatUI() {
// All TaskListeners should support compat UI except PIP.
return true;
}
- /** Attaches the a child window surface to the task surface. */
+ /** Attaches a child window surface to the task surface. */
default void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
throw new IllegalStateException(
"This task listener doesn't support child surface attachment.");
}
+ /** Reparents a child window surface to the task surface. */
+ default void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ throw new IllegalStateException(
+ "This task listener doesn't support child surface reparent.");
+ }
default void dump(@NonNull PrintWriter pw, String prefix) {};
}
@@ -620,6 +626,23 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
updateCameraCompatControlState(info.getTaskInfo().token, state);
}
+ /** Reparents a child window surface to the task surface. */
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ final TaskListener taskListener;
+ synchronized (mLock) {
+ taskListener = mTasks.contains(taskId)
+ ? getTaskListener(mTasks.get(taskId).getTaskInfo())
+ : null;
+ }
+ if (taskListener == null) {
+ ProtoLog.w(WM_SHELL_TASK_ORG, "Failed to find Task to reparent surface taskId=%d",
+ taskId);
+ return;
+ }
+ taskListener.reparentChildSurfaceToTask(taskId, sc, t);
+ }
+
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 54e743f72cc6..ca4ef07eb1db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -367,10 +367,20 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- if (mTaskInfo.taskId != taskId) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
+ if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
- b.setParent(mTaskLeash);
+ return mTaskLeash;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index e344c3bea42b..33eec335bca3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -275,12 +275,22 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
if (getRootTaskId() == taskId) {
- b.setParent(mRootTaskLeash);
+ return mRootTaskLeash;
} else if (getTaskId1() == taskId) {
- b.setParent(mTaskLeash1);
+ return mTaskLeash1;
} else if (getTaskId2() == taskId) {
- b.setParent(mTaskLeash2);
+ return mTaskLeash2;
} else {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 79e624212f4b..3876533a922e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -173,8 +173,8 @@ public class BadgedImageView extends ConstraintLayout {
}
@Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ public void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
if (!shouldDrawDot()) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index 99dbfe01964c..b87cf47dd93f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -24,9 +24,12 @@ import com.android.wm.shell.common.annotations.ExternalThread;
@ExternalThread
public interface CompatUI {
/**
- * Called when the keyguard occluded state changes. Removes all compat UIs if the
- * keyguard is now occluded.
- * @param occluded indicates if the keyguard is now occluded.
+ * Called when the keyguard showing state changes. Removes all compat UIs if the
+ * keyguard is now showing.
+ *
+ * <p>Note that if the keyguard is occluded it will also be considered showing.
+ *
+ * @param showing indicates if the keyguard is now showing.
*/
- void onKeyguardOccludedChanged(boolean occluded);
+ void onKeyguardShowingChanged(boolean showing);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index ee4d5ed018bf..b2bbafeb7bf5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -109,9 +109,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
// Only show each hint once automatically in the process life.
private final CompatUIHintsState mCompatUIHintsState;
- // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
// be shown.
- private boolean mKeyguardOccluded;
+ private boolean mKeyguardShowing;
public CompatUIController(Context context,
DisplayController displayController,
@@ -218,14 +218,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
}
@VisibleForTesting
- void onKeyguardOccludedChanged(boolean occluded) {
- mKeyguardOccluded = occluded;
- // Hide the compat UIs when keyguard is occluded.
+ void onKeyguardShowingChanged(boolean showing) {
+ mKeyguardShowing = showing;
+ // Hide the compat UIs when keyguard is showing.
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
private boolean showOnDisplay(int displayId) {
- return !mKeyguardOccluded && !isImeShowingOnDisplay(displayId);
+ return !mKeyguardShowing && !isImeShowingOnDisplay(displayId);
}
private boolean isImeShowingOnDisplay(int displayId) {
@@ -372,9 +372,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
@ExternalThread
private class CompatUIImpl implements CompatUI {
@Override
- public void onKeyguardOccludedChanged(boolean occluded) {
+ public void onKeyguardShowingChanged(boolean showing) {
mMainExecutor.execute(() -> {
- CompatUIController.this.onKeyguardOccludedChanged(occluded);
+ CompatUIController.this.onKeyguardShowingChanged(showing);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index 2da6a6bc70c0..8aa4d0ee99ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -38,6 +39,7 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
// 204 is simply 255 * 0.8.
static final int BACKGROUND_DIM_ALPHA = 204;
private View mDialogContainer;
+ private TextView mDialogTitle;
private Drawable mBackgroundDim;
public LetterboxEduDialogLayout(Context context) {
@@ -61,6 +63,10 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
return mDialogContainer;
}
+ TextView getDialogTitle() {
+ return mDialogTitle;
+ }
+
Drawable getBackgroundDim() {
return mBackgroundDim;
}
@@ -84,6 +90,7 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mDialogContainer = findViewById(R.id.letterbox_education_dialog_container);
+ mDialogTitle = findViewById(R.id.letterbox_education_dialog_title);
mBackgroundDim = getBackground().mutate();
// Set the alpha of the background dim to 0 for enter animation.
mBackgroundDim.setAlpha(0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index 30b9f0838e10..dda72ffb432f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -28,6 +28,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -132,7 +133,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
updateDialogMargins();
mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
- this::setDismissOnClickListener);
+ this::onDialogEnterAnimationEnded);
return mLayout;
}
@@ -157,11 +158,13 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
R.layout.letterbox_education_dialog_layout, null);
}
- private void setDismissOnClickListener() {
+ private void onDialogEnterAnimationEnded() {
if (mLayout == null) {
return;
}
mLayout.setDismissOnClickListener(this::onDismiss);
+ // Focus on the dialog title for accessibility.
+ mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
private void onDismiss() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 491dff08187f..1e934c5c6e22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -27,7 +27,6 @@ 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.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipMediaController;
@@ -163,15 +162,14 @@ public abstract class TvPipModule {
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
+ Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, tvPipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ pipTransitionController, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
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 7879e7a5bb00..73f393140cbc 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
@@ -285,15 +285,14 @@ public class WMShellModule {
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
+ Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ pipTransitionController, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 52ff21bc3172..fef9be36a35f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -110,6 +110,24 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
+ if (!mTasks.contains(taskId)) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ return mTasks.get(taskId).mLeash;
+ }
+
+ @Override
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 6e38e421d4b6..73e6cba43ec0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -133,10 +133,20 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
if (!mDataByTaskId.contains(taskId)) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
- b.setParent(mDataByTaskId.get(taskId).surface);
+ return mDataByTaskId.get(taskId).surface;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index 86bf3ff1cea9..d2f42c39acd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -343,10 +343,20 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
if (!mLeashByTaskId.contains(taskId)) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
- b.setParent(mLeashByTaskId.get(taskId));
+ return mLeashByTaskId.get(taskId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index ddc85f758916..e03421dd58ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -47,12 +47,13 @@ interface IPip {
/**
* Notifies the swiping Activity to PiP onto home transition is finished
*
+ * @param taskId the Task id that the Activity and overlay are currently in.
* @param componentName ComponentName represents the Activity
* @param destinationBounds the destination bounds the PiP window lands into
* @param overlay an optional overlay to fade out after entering PiP
*/
- oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds,
- in SurfaceControl overlay) = 2;
+ oneway void stopSwipePipToHome(int taskId, in ComponentName componentName,
+ in Rect destinationBounds, in SurfaceControl overlay) = 2;
/**
* Sets listener to get pinned stack animation callbacks.
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 f9ec88659eea..b266189ec262 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
@@ -81,7 +81,6 @@ import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -134,7 +133,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -250,7 +248,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* An optional overlay used to mask content changing between an app in/out of PiP, only set if
* {@link PipTransitionState#getInSwipePipToHomeTransition()} is true.
*/
- private SurfaceControl mSwipePipToHomeOverlay;
+ @Nullable
+ SurfaceControl mSwipePipToHomeOverlay;
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@@ -261,7 +260,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@@ -284,7 +282,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -357,12 +354,24 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* Callback when launcher finishes swipe-pip-to-home operation.
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
SurfaceControl overlay) {
// do nothing if there is no startSwipePipToHome being called before
- if (mPipTransitionState.getInSwipePipToHomeTransition()) {
- mPipBoundsState.setBounds(destinationBounds);
- mSwipePipToHomeOverlay = overlay;
+ if (!mPipTransitionState.getInSwipePipToHomeTransition()) {
+ return;
+ }
+ mPipBoundsState.setBounds(destinationBounds);
+ mSwipePipToHomeOverlay = overlay;
+ if (ENABLE_SHELL_TRANSITIONS) {
+ // With Shell transition, the overlay was attached to the remote transition leash, which
+ // will be removed when the current transition is finished, so we need to reparent it
+ // to the actual Task surface now.
+ // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
+ // transition.
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t);
+ t.setLayer(overlay, Integer.MAX_VALUE);
+ t.apply();
}
}
@@ -492,11 +501,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mLegacySplitScreenOptional.ifPresent(splitScreen -> {
- if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
- }
- });
}
/**
@@ -682,7 +686,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void onEndOfSwipePipToHomeTransition() {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mSwipePipToHomeOverlay = null;
return;
}
@@ -837,6 +840,24 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
+ if (mTaskInfo == null || mLeash == null || mTaskInfo.taskId != taskId) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ return mLeash;
+ }
+
+ @Override
public void onFixedRotationStarted(int displayId, int newRotation) {
mNextRotation = newRotation;
mWaitForFixedRotation = true;
@@ -891,9 +912,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
clearWaitForFixedRotation();
}
- /** Called when exiting PIP tranisiton is finished to do the state cleanup. */
+ /** Called when exiting PIP transition is finished to do the state cleanup. */
void onExitPipFinished(TaskInfo info) {
clearWaitForFixedRotation();
+ if (mSwipePipToHomeOverlay != null) {
+ removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */);
+ mSwipePipToHomeOverlay = null;
+ }
mPipTransitionState.setInSwipePipToHomeTransition(false);
mPictureInPictureParams = null;
mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
@@ -1478,36 +1503,21 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
- * bounds if PiP is going to split screen.
+ * Sync with {@link SplitScreenController} on destination bounds if PiP is going to
+ * split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
- if (enterSplit && mSplitScreenOptional.isPresent()) {
- final Rect topLeft = new Rect();
- final Rect bottomRight = new Rect();
- mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
- final boolean isPipTopLeft = isPipTopLeft();
- destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
- return true;
- }
-
- if (!mLegacySplitScreenOptional.isPresent()) {
+ if (!enterSplit || !mSplitScreenOptional.isPresent()) {
return false;
}
-
- LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
- if (!legacySplitScreen.isDividerVisible()) {
- // fail early if system is not in split screen mode
- return false;
- }
-
- // PiP window will go to split-secondary mode instead of fullscreen, populates the
- // split screen bounds here.
- destinationBoundsOut.set(legacySplitScreen.getDividerView()
- .getNonMinimizedSplitScreenSecondaryBounds());
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+ final boolean isPipTopLeft = isPipTopLeft();
+ destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 4cab6d336892..be713a59a816 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -152,7 +152,7 @@ public class PipTransition extends PipTransitionController {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+ final TransitionInfo.Change currentPipTaskChange = findCurrentPipTaskChange(info);
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
mInFixedRotation = fixedRotationChange != null;
mEndFixedRotation = mInFixedRotation
@@ -172,22 +172,29 @@ public class PipTransition extends PipTransitionController {
throw new RuntimeException("Previous callback not called, aborting exit PIP.");
}
- if (currentPipChange == null) {
- throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
+ // PipTaskChange can be null if the PIP task has been detached, for example, when the
+ // task contains multiple activities, the PIP will be moved to a new PIP task when
+ // entering, and be moved back when exiting. In that case, the PIP task will be removed
+ // immediately.
+ final TaskInfo pipTaskInfo = currentPipTaskChange != null
+ ? currentPipTaskChange.getTaskInfo()
+ : mPipOrganizer.getTaskInfo();
+ if (pipTaskInfo == null) {
+ throw new RuntimeException("Cannot find the pip task for exit-pip transition.");
}
switch (type) {
case TRANSIT_EXIT_PIP:
startExitAnimation(info, startTransaction, finishTransaction, finishCallback,
- currentPipChange);
+ pipTaskInfo, currentPipTaskChange);
break;
case TRANSIT_EXIT_PIP_TO_SPLIT:
startExitToSplitAnimation(info, startTransaction, finishTransaction,
- finishCallback, currentPipChange);
+ finishCallback, pipTaskInfo);
break;
case TRANSIT_REMOVE_PIP:
removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
- currentPipChange);
+ pipTaskInfo);
break;
default:
throw new IllegalStateException("mExitTransition with unexpected transit type="
@@ -200,9 +207,9 @@ public class PipTransition extends PipTransitionController {
// The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
// happen when a new activity requests enter PIP). In this case, we just show this Task in
// its end state, and play other animation as normal.
- if (currentPipChange != null
- && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
- resetPrevPip(currentPipChange, startTransaction);
+ if (currentPipTaskChange != null
+ && currentPipTaskChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
+ resetPrevPip(currentPipTaskChange, startTransaction);
}
// Entering PIP.
@@ -212,8 +219,9 @@ public class PipTransition extends PipTransitionController {
// For transition that we don't animate, but contains the PIP leash, we need to update the
// PIP surface, otherwise it will be reset after the transition.
- if (currentPipChange != null) {
- updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
+ if (currentPipTaskChange != null) {
+ updatePipForUnhandledTransition(currentPipTaskChange, startTransaction,
+ finishTransaction);
}
// Fade in the fadeout PIP when the fixed rotation is finished.
@@ -322,7 +330,7 @@ public class PipTransition extends PipTransitionController {
}
@Nullable
- private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
+ private TransitionInfo.Change findCurrentPipTaskChange(@NonNull TransitionInfo info) {
if (mCurrentPipTaskToken == null) {
return null;
}
@@ -350,9 +358,30 @@ public class PipTransition extends PipTransitionController {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull TransitionInfo.Change pipChange) {
+ @NonNull TaskInfo taskInfo, @Nullable TransitionInfo.Change pipTaskChange) {
+ TransitionInfo.Change pipChange = pipTaskChange;
+ if (pipChange == null) {
+ // The pipTaskChange is null, this can happen if we are reparenting the PIP activity
+ // back to its original Task. In that case, we should animate the activity leash
+ // instead, which should be the only non-task, independent, TRANSIT_CHANGE window.
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() == null && change.getMode() == TRANSIT_CHANGE
+ && TransitionInfo.isIndependent(change, info)) {
+ pipChange = change;
+ break;
+ }
+ }
+ }
+ if (pipChange == null) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: No window of exiting PIP is found. Can't play expand animation", TAG);
+ removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
+ taskInfo);
+ return;
+ }
mFinishCallback = (wct, wctCB) -> {
- mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(wct, wctCB);
};
mFinishTransaction = finishTransaction;
@@ -372,7 +401,7 @@ public class PipTransition extends PipTransitionController {
if (displayRotationChange != null) {
// Exiting PIP to fullscreen with orientation change.
startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
- displayRotationChange, pipChange);
+ displayRotationChange, taskInfo, pipChange);
return;
}
}
@@ -413,15 +442,14 @@ public class PipTransition extends PipTransitionController {
} else {
rotationDelta = Surface.ROTATION_0;
}
- startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds,
- rotationDelta);
+ startExpandAnimation(taskInfo, pipChange.getLeash(), destinationBounds, rotationDelta);
}
private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionInfo.Change displayRotationChange,
- @NonNull TransitionInfo.Change pipChange) {
+ @NonNull TaskInfo taskInfo, @NonNull TransitionInfo.Change pipChange) {
final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
displayRotationChange.getEndRotation());
@@ -459,7 +487,7 @@ public class PipTransition extends PipTransitionController {
// Expand and rotate the pip window to fullscreen.
final PipAnimationController.PipTransitionAnimator animator =
- mPipAnimationController.getAnimator(pipChange.getTaskInfo(), pipChange.getLeash(),
+ mPipAnimationController.getAnimator(taskInfo, pipChange.getLeash(),
startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP,
0 /* startingAngle */, pipRotateDelta);
animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
@@ -485,11 +513,11 @@ public class PipTransition extends PipTransitionController {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull TransitionInfo.Change pipChange) {
+ @NonNull TaskInfo taskInfo) {
startTransaction.apply();
finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
mPipBoundsState.getDisplayBounds());
- mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null, null);
}
@@ -604,11 +632,18 @@ public class PipTransition extends PipTransitionController {
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
- SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
+ final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
+ startTransaction.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
.setPosition(leash, destinationBounds.left, destinationBounds.top)
.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
- startTransaction.merge(tx);
+ if (swipePipToHomeOverlay != null) {
+ // Launcher fade in the overlay on top of the fullscreen Task. It is possible we
+ // reparent the PIP activity to a new PIP task (in case there are other activities
+ // in the original Task), so we should also reparent the overlay to the PIP task.
+ startTransaction.reparent(swipePipToHomeOverlay, leash)
+ .setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE);
+ mPipOrganizer.mSwipePipToHomeOverlay = null;
+ }
startTransaction.apply();
if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
// For fixed rotation, set the destination bounds to the new rotation coordinates
@@ -618,6 +653,10 @@ public class PipTransition extends PipTransitionController {
mPipBoundsState.setBounds(destinationBounds);
onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */);
sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ if (swipePipToHomeOverlay != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay,
+ null /* callback */, false /* withStartDelay */);
+ }
mPipTransitionState.setInSwipePipToHomeTransition(false);
return true;
}
@@ -678,11 +717,11 @@ public class PipTransition extends PipTransitionController {
}
}
- private void startExitToSplitAnimation(TransitionInfo info,
- SurfaceControl.Transaction startTransaction,
- SurfaceControl.Transaction finishTransaction,
- Transitions.TransitionFinishCallback finishCallback,
- TransitionInfo.Change pipChange) {
+ private void startExitToSplitAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TaskInfo taskInfo) {
final int changeSize = info.getChanges().size();
if (changeSize < 4) {
throw new RuntimeException(
@@ -710,15 +749,15 @@ public class PipTransition extends PipTransitionController {
mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
startTransaction.apply();
- mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null, null);
}
- private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange,
+ private void resetPrevPip(@NonNull TransitionInfo.Change prevPipTaskChange,
@NonNull SurfaceControl.Transaction startTransaction) {
- final SurfaceControl leash = prevPipChange.getLeash();
- final Rect bounds = prevPipChange.getEndAbsBounds();
- final Point offset = prevPipChange.getEndRelOffset();
+ final SurfaceControl leash = prevPipTaskChange.getLeash();
+ final Rect bounds = prevPipTaskChange.getEndAbsBounds();
+ final Point offset = prevPipTaskChange.getEndRelOffset();
bounds.offset(-offset.x, -offset.y);
startTransaction.setWindowCrop(leash, null);
@@ -726,7 +765,7 @@ public class PipTransition extends PipTransitionController {
startTransaction.setCornerRadius(leash, 0);
startTransaction.setPosition(leash, bounds.left, bounds.top);
- if (mHasFadeOut && prevPipChange.getTaskInfo().isVisible()) {
+ if (mHasFadeOut && prevPipTaskChange.getTaskInfo().isVisible()) {
if (mPipAnimationController.getCurrentAnimator() != null) {
mPipAnimationController.getCurrentAnimator().cancel();
}
@@ -734,7 +773,7 @@ public class PipTransition extends PipTransitionController {
}
mHasFadeOut = false;
mCurrentPipTaskToken = null;
- mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
+ mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
}
private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
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 5b6c95eef561..e7a1c4cca07e 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
@@ -595,9 +595,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return entryBounds;
}
- private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+ private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
SurfaceControl overlay) {
- mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds, overlay);
+ mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
}
private String getTransitionTag(int direction) {
@@ -928,11 +928,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay) {
+ public void stopSwipePipToHome(int taskId, ComponentName componentName,
+ Rect destinationBounds, SurfaceControl overlay) {
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> {
- controller.stopSwipePipToHome(componentName, destinationBounds, overlay);
+ controller.stopSwipePipToHome(taskId, componentName, destinationBounds,
+ overlay);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 1a5e3f26ab37..9e5359332055 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1117,9 +1117,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return SPLIT_POSITION_UNDEFINED;
}
- if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
+ if (mMainStage.containsToken(token)) {
return getMainStagePosition();
- } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
+ } else if (mSideStage.containsToken(token)) {
return getSideStagePosition();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index c5aab45f56fa..5f0cd01f5416 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -124,6 +124,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return mChildrenTaskInfo.contains(taskId);
}
+ boolean containsToken(WindowContainerToken token) {
+ if (token.equals(mRootTaskInfo.token)) {
+ return true;
+ }
+
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ if (token.equals(mChildrenTaskInfo.valueAt(i).token)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Returns the top visible child task's id.
*/
@@ -280,10 +294,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
if (mRootTaskInfo.taskId == taskId) {
- b.setParent(mRootLeash);
+ return mRootLeash;
} else if (mChildrenLeashes.contains(taskId)) {
- b.setParent(mChildrenLeashes.get(taskId));
+ return mChildrenLeashes.get(taskId);
} else {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java
index 8b36c9406b15..7b679580fa87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java
@@ -227,10 +227,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
if (mRootTaskInfo.taskId == taskId) {
- b.setParent(mRootLeash);
+ return mRootLeash;
} else if (mChildrenLeashes.contains(taskId)) {
- b.setParent(mChildrenLeashes.get(taskId));
+ return mChildrenLeashes.get(taskId);
} else {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 56d51687603a..a225f4ec8349 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -408,7 +408,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|| type == TRANSIT_CLOSE
|| type == TRANSIT_TO_FRONT
|| type == TRANSIT_TO_BACK;
- if (isOpenOrCloseTransition) {
+ final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
+ if (isOpenOrCloseTransition && !isTranslucent) {
// Use the overview background as the background for the animation
final Context uiContext = ActivityThread.currentActivityThread()
.getSystemUiContext();
@@ -729,14 +730,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
: R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
} else if (type == TRANSIT_OPEN) {
- if (isTask) {
+ // We will translucent open animation for translucent activities and tasks. Choose
+ // WindowAnimation_activityOpenEnterAnimation and set translucent here, then
+ // TransitionAnimation loads appropriate animation later.
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+ translucent = true;
+ }
+ if (isTask && !translucent) {
animAttr = enter
? R.styleable.WindowAnimation_taskOpenEnterAnimation
: R.styleable.WindowAnimation_taskOpenExitAnimation;
} else {
- if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
- translucent = true;
- }
animAttr = enter
? R.styleable.WindowAnimation_activityOpenEnterAnimation
: R.styleable.WindowAnimation_activityOpenExitAnimation;
@@ -770,7 +774,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
.loadAnimationAttr(options.getPackageName(), options.getAnimations(),
animAttr, translucent);
} else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr);
+ a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr, translucent);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index efb52a5b4644..fb3cd87448d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -73,9 +73,9 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Set to {@code true} to enable shell transitions. */
public static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
- && SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false);
+ && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
/** Transition type for exiting PIP via the Shell, via pressing the expand button. */
public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
@@ -363,7 +363,9 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
}
- // apply transfer starting window directly if there is no other task change.
+ // apply transfer starting window directly if there is no other task change. Since this
+ // is an activity->activity situation, we can detect it by selecting transitions with only
+ // 2 changes where neither are tasks and one is a starting-window recipient.
final int changeSize = info.getChanges().size();
if (changeSize == 2) {
boolean nonTaskChange = true;
@@ -380,7 +382,9 @@ public class Transitions implements RemoteCallable<Transitions> {
}
if (nonTaskChange && transferStartingWindow) {
t.apply();
- onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ // Treat this as an abort since we are bypassing any merge logic and effectively
+ // finishing immediately.
+ onAbort(transitionToken);
return;
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 57bc0d580d72..3dd9e0572947 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -61,7 +61,7 @@ abstract class BaseAppHelper(
private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
fun isShellTransitionsEnabled() =
- SystemProperties.getBoolean("persist.debug.shell_transit", false)
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false)
fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
try {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 29e40be457d1..a31b28737552 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -325,17 +325,17 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
- public void testChangeLayoutsVisibilityOnKeyguardOccludedChanged() {
+ public void testChangeLayoutsVisibilityOnKeyguardShowingChanged() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- // Verify that the restart button is hidden after keyguard becomes occluded.
- mController.onKeyguardOccludedChanged(true);
+ // Verify that the restart button is hidden after keyguard becomes showing.
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
- // Verify button remains hidden while keyguard is occluded.
+ // Verify button remains hidden while keyguard is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
@@ -345,20 +345,20 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button is shown after keyguard becomes not showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
}
@Test
- public void testLayoutsRemainHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+ public void testLayoutsRemainHiddenOnKeyguardShowingFalseWhenImeIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardOccludedChanged(true);
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -366,8 +366,8 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockCompatLayout);
clearInvocations(mMockLetterboxEduLayout);
- // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button remains hidden after keyguard becomes not showing since IME is showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
@@ -380,12 +380,12 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
- public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsOccluded() {
+ public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardOccludedChanged(true);
+ mController.onKeyguardShowingChanged(true);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -393,14 +393,14 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockCompatLayout);
clearInvocations(mMockLetterboxEduLayout);
- // Verify button remains hidden after IME is hidden since keyguard is occluded.
+ // Verify button remains hidden after IME is hidden since keyguard is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
- // Verify button is shown after keyguard becomes not occluded.
- mController.onKeyguardOccludedChanged(false);
+ // Verify button is shown after keyguard becomes not showing.
+ mController.onKeyguardShowingChanged(false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
index 00e4938d866c..1dee88c43806 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -70,6 +70,8 @@ public class LetterboxEduDialogLayoutTest extends ShellTestCase {
public void testOnFinishInflate() {
assertEquals(mLayout.getDialogContainer(),
mLayout.findViewById(R.id.letterbox_education_dialog_container));
+ assertEquals(mLayout.getDialogTitle(),
+ mLayout.findViewById(R.id.letterbox_education_dialog_title));
assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
assertEquals(mLayout.getBackground().getAlpha(), 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
index 0509dd38abe0..337b7385faec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -41,9 +41,11 @@ import android.testing.AndroidTestingRunner;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.SurfaceControlViewHost;
+import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import androidx.test.filters.SmallTest;
@@ -173,13 +175,19 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
verifyLayout(layout, mWindowAttrsCaptor.getValue(), /* expectedWidth= */ TASK_WIDTH,
/* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */ DISPLAY_CUTOUT_TOP,
/* expectedExtraBottomMargin= */ DISPLAY_CUTOUT_BOTTOM);
+ View dialogTitle = layout.getDialogTitle();
+ assertNotNull(dialogTitle);
+ spyOn(dialogTitle);
// Clicking the layout does nothing until enter animation is done.
layout.performClick();
verify(mAnimationController, never()).startExitAnimation(any(), any());
+ // The dialog title shouldn't be focused for Accessibility until enter animation is done.
+ verify(dialogTitle, never()).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
verifyAndFinishEnterAnimation(layout);
+ verify(dialogTitle).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
// Exit animation should start following a click on the layout.
layout.performClick();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
index 46fe201072c9..4523e2c9cba5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
@@ -50,7 +50,7 @@ import java.util.Optional;
@SmallTest
public class FullscreenTaskListenerTest {
private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
@Mock
private SyncTransactionQueue mSyncQueue;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0172cf324eea..14d9fb9babc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -48,7 +48,6 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -76,7 +75,6 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
@Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
@@ -101,8 +99,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalLegacySplitScreen,
- mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+ mMockPipTransitionController, mMockOptionalSplitScreen,
+ mMockDisplayController, mMockPipUiEventLogger,
mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 13b726efb046..157c30bcb6c7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -62,7 +62,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests extends ShellTestCase {
private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
@Mock
private ShellTaskOrganizer mTaskOrganizer;
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index fd6e6e932ebc..055dbb2b5c5e 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -442,14 +442,13 @@ public:
reply.readExceptionCode();
}
- void mountObb(const String16& rawPath, const String16& canonicalPath, const String16& key,
+ void mountObb(const String16& rawPath, const String16& canonicalPath,
const sp<IObbActionListener>& token, int32_t nonce, const sp<ObbInfo>& obbInfo)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
data.writeString16(rawPath);
data.writeString16(canonicalPath);
- data.writeString16(key);
data.writeStrongBinder(IInterface::asBinder(token));
data.writeInt32(nonce);
obbInfo->writeToParcel(&data);
diff --git a/libs/storage/include/storage/IMountService.h b/libs/storage/include/storage/IMountService.h
index 2463e023efc1..5b07318f4432 100644
--- a/libs/storage/include/storage/IMountService.h
+++ b/libs/storage/include/storage/IMountService.h
@@ -64,8 +64,8 @@ public:
virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
virtual void finishMediaUpdate() = 0;
virtual void mountObb(const String16& rawPath, const String16& canonicalPath,
- const String16& key, const sp<IObbActionListener>& token,
- const int32_t nonce, const sp<ObbInfo>& obbInfo) = 0;
+ const sp<IObbActionListener>& token, const int32_t nonce,
+ const sp<ObbInfo>& obbInfo) = 0;
virtual void unmountObb(const String16& filename, const bool force,
const sp<IObbActionListener>& token, const int32_t nonce) = 0;
virtual bool isObbMounted(const String16& filename) = 0;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3a2f0d9194f5..432196c9e4ef 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8450,6 +8450,22 @@ public class AudioManager {
}
}
+ /**
+ * Returns the audio HAL version in the form MAJOR.MINOR. If there is no audio HAL found, null
+ * will be returned.
+ *
+ * @hide
+ */
+ @TestApi
+ public static @Nullable String getHalVersion() {
+ try {
+ return getService().getHalVersion();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying getHalVersion", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final Object mMuteAwaitConnectionListenerLock = new Object();
@GuardedBy("mMuteAwaitConnectionListenerLock")
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fa3057a4ead1..bdbb740a849b 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -480,7 +480,6 @@ interface IAudioService {
boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
-
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
void addAssistantServicesUids(in int[] assistantUID);
@@ -501,4 +500,6 @@ interface IAudioService {
in IAudioDeviceVolumeDispatcher cb,
in String packageName,
in AudioDeviceAttributes device, in List<VolumeInfo> volumes);
+
+ String getHalVersion();
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 311476cbd489..47e402f73f40 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -76,6 +76,7 @@ public final class MediaRouter2 {
@GuardedBy("sSystemRouterLock")
private static Map<String, MediaRouter2> sSystemMediaRouter2Map = new ArrayMap<>();
+
private static MediaRouter2Manager sManager;
@GuardedBy("sRouterLock")
@@ -119,14 +120,12 @@ public final class MediaRouter2 {
private final AtomicInteger mNextRequestId = new AtomicInteger(1);
final Handler mHandler;
- @GuardedBy("mLock")
- private boolean mShouldUpdateRoutes = true;
+
+ private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>();
private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
private volatile OnGetControllerHintsListener mOnGetControllerHintsListener;
- /**
- * Gets an instance of the media router associated with the context.
- */
+ /** Gets an instance of the media router associated with the context. */
@NonNull
public static MediaRouter2 getInstance(@NonNull Context context) {
Objects.requireNonNull(context, "context must not be null");
@@ -139,29 +138,31 @@ public final class MediaRouter2 {
}
/**
- * Gets an instance of the system media router which controls the app's media routing.
- * Returns {@code null} if the given package name is invalid.
- * There are several things to note when using the media routers created with this method.
- * <p>
- * First of all, the discovery preference passed to {@link #registerRouteCallback}
- * will have no effect. The callback will be called accordingly with the client app's
- * discovery preference. Therefore, it is recommended to pass
- * {@link RouteDiscoveryPreference#EMPTY} there.
- * <p>
- * Also, do not keep/compare the instances of the {@link RoutingController}, since they are
+ * Gets an instance of the system media router which controls the app's media routing. Returns
+ * {@code null} if the given package name is invalid. There are several things to note when
+ * using the media routers created with this method.
+ *
+ * <p>First of all, the discovery preference passed to {@link #registerRouteCallback} will have
+ * no effect. The callback will be called accordingly with the client app's discovery
+ * preference. Therefore, it is recommended to pass {@link RouteDiscoveryPreference#EMPTY}
+ * there.
+ *
+ * <p>Also, do not keep/compare the instances of the {@link RoutingController}, since they are
* always newly created with the latest session information whenever below methods are called:
+ *
* <ul>
- * <li> {@link #getControllers()} </li>
- * <li> {@link #getController(String)}} </li>
- * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li>
- * <li> {@link TransferCallback#onStop(RoutingController)} </li>
- * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li>
+ * <li>{@link #getControllers()}
+ * <li>{@link #getController(String)}}
+ * <li>{@link TransferCallback#onTransfer(RoutingController, RoutingController)}
+ * <li>{@link TransferCallback#onStop(RoutingController)}
+ * <li>{@link ControllerCallback#onControllerUpdated(RoutingController)}
* </ul>
+ *
* Therefore, in order to track the current routing status, keep the controller's ID instead,
- * and use {@link #getController(String)} and {@link #getSystemController()} for
- * getting controllers.
- * <p>
- * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}.
+ * and use {@link #getController(String)} and {@link #getSystemController()} for getting
+ * controllers.
+ *
+ * <p>Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}.
*
* @param clientPackageName the package name of the app to control
* @throws SecurityException if the caller doesn't have MODIFY_AUDIO_ROUTING permission.
@@ -170,15 +171,16 @@ public final class MediaRouter2 {
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@Nullable
- public static MediaRouter2 getInstance(@NonNull Context context,
- @NonNull String clientPackageName) {
+ public static MediaRouter2 getInstance(
+ @NonNull Context context, @NonNull String clientPackageName) {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
// Note: Even though this check could be somehow bypassed, the other permission checks
// in system server will not allow MediaRouter2Manager to be registered.
- IMediaRouterService serviceBinder = IMediaRouterService.Stub.asInterface(
- ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+ IMediaRouterService serviceBinder =
+ IMediaRouterService.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
try {
// SecurityException will be thrown if there's no permission.
serviceBinder.enforceMediaContentControlPermission();
@@ -212,17 +214,17 @@ public final class MediaRouter2 {
/**
* Starts scanning remote routes.
- * <p>
- * Route discovery can happen even when the {@link #startScan()} is not called.
- * This is because the scanning could be started before by other apps.
- * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
- * that the routes found before are removed and added again.
- * <p>
- * Use {@link RouteCallback} to get the route related events.
- * <p>
- * Note that calling start/stopScan is applied to all system routers in the same process.
- * <p>
- * This will be no-op for non-system media routers.
+ *
+ * <p>Route discovery can happen even when the {@link #startScan()} is not called. This is
+ * because the scanning could be started before by other apps. Therefore, calling this method
+ * after calling {@link #stopScan()} does not necessarily mean that the routes found before are
+ * removed and added again.
+ *
+ * <p>Use {@link RouteCallback} to get the route related events.
+ *
+ * <p>Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * <p>This will be no-op for non-system media routers.
*
* @see #stopScan()
* @see #getInstance(Context, String)
@@ -238,18 +240,17 @@ public final class MediaRouter2 {
/**
* Stops scanning remote routes to reduce resource consumption.
- * <p>
- * Route discovery can be continued even after this method is called.
- * This is because the scanning is only turned off when all the apps stop scanning.
- * Therefore, calling this method does not necessarily mean the routes are removed.
- * Also, for the same reason it does not mean that {@link RouteCallback#onRoutesAdded(List)}
- * is not called afterwards.
- * <p>
- * Use {@link RouteCallback} to get the route related events.
- * <p>
- * Note that calling start/stopScan is applied to all system routers in the same process.
- * <p>
- * This will be no-op for non-system media routers.
+ *
+ * <p>Route discovery can be continued even after this method is called. This is because the
+ * scanning is only turned off when all the apps stop scanning. Therefore, calling this method
+ * does not necessarily mean the routes are removed. Also, for the same reason it does not mean
+ * that {@link RouteCallback#onRoutesAdded(List)} is not called afterwards.
+ *
+ * <p>Use {@link RouteCallback} to get the route related events.
+ *
+ * <p>Note that calling start/stopScan is applied to all system routers in the same process.
+ *
+ * <p>This will be no-op for non-system media routers.
*
* @see #startScan()
* @see #getInstance(Context, String)
@@ -265,8 +266,9 @@ public final class MediaRouter2 {
private MediaRouter2(Context appContext) {
mContext = appContext;
- mMediaRouterService = IMediaRouterService.Stub.asInterface(
- ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+ mMediaRouterService =
+ IMediaRouterService.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
mPackageName = mContext.getPackageName();
mHandler = new Handler(Looper.getMainLooper());
@@ -302,9 +304,10 @@ public final class MediaRouter2 {
mClientPackageName = clientPackageName;
mManagerCallback = new ManagerCallback();
mHandler = new Handler(Looper.getMainLooper());
- mSystemController = new SystemRoutingController(
- ensureClientPackageNameForSystemSession(
- sManager.getSystemRoutingSession(clientPackageName)));
+ mSystemController =
+ new SystemRoutingController(
+ ensureClientPackageNameForSystemSession(
+ sManager.getSystemRoutingSession(clientPackageName)));
mDiscoveryPreference = sManager.getDiscoveryPreference(clientPackageName);
updateAllRoutesFromManager();
@@ -318,8 +321,8 @@ public final class MediaRouter2 {
*
* @hide
*/
- static boolean checkRouteListContainsRouteId(@NonNull List<MediaRoute2Info> routeList,
- @NonNull String routeId) {
+ static boolean checkRouteListContainsRouteId(
+ @NonNull List<MediaRoute2Info> routeList, @NonNull String routeId) {
for (MediaRoute2Info info : routeList) {
if (TextUtils.equals(routeId, info.getId())) {
return true;
@@ -330,8 +333,8 @@ public final class MediaRouter2 {
/**
* Gets the client package name of the app which this media router controls.
- * <p>
- * This will return null for non-system media routers.
+ *
+ * <p>This will return null for non-system media routers.
*
* @see #getInstance(Context, String)
* @hide
@@ -344,12 +347,12 @@ public final class MediaRouter2 {
/**
* Registers a callback to discover routes and to receive events when they change.
- * <p>
- * If the specified callback is already registered, its registration will be updated for the
+ *
+ * <p>If the specified callback is already registered, its registration will be updated for the
* given {@link Executor executor} and {@link RouteDiscoveryPreference discovery preference}.
- * </p>
*/
- public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
+ public void registerRouteCallback(
+ @NonNull @CallbackExecutor Executor executor,
@NonNull RouteCallback routeCallback,
@NonNull RouteDiscoveryPreference preference) {
Objects.requireNonNull(executor, "executor must not be null");
@@ -391,8 +394,8 @@ public final class MediaRouter2 {
}
/**
- * Unregisters the given callback. The callback will no longer receive events.
- * If the callback has not been added or been removed already, it is ignored.
+ * Unregisters the given callback. The callback will no longer receive events. If the callback
+ * has not been added or been removed already, it is ignored.
*
* @param routeCallback the callback to unregister
* @see #registerRouteCallback
@@ -400,8 +403,7 @@ public final class MediaRouter2 {
public void unregisterRouteCallback(@NonNull RouteCallback routeCallback) {
Objects.requireNonNull(routeCallback, "callback must not be null");
- if (!mRouteCallbackRecords.remove(
- new RouteCallbackRecord(null, routeCallback, null))) {
+ if (!mRouteCallbackRecords.remove(new RouteCallbackRecord(null, routeCallback, null))) {
Log.w(TAG, "unregisterRouteCallback: Ignoring unknown callback");
return;
}
@@ -416,8 +418,7 @@ public final class MediaRouter2 {
}
if (updateDiscoveryPreferenceIfNeededLocked()) {
try {
- mMediaRouterService.setDiscoveryRequestWithRouter2(
- mStub, mDiscoveryPreference);
+ mMediaRouterService.setDiscoveryRequestWithRouter2(mStub, mDiscoveryPreference);
} catch (RemoteException ex) {
Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.", ex);
}
@@ -430,27 +431,28 @@ public final class MediaRouter2 {
}
mStub = null;
}
- mShouldUpdateRoutes = true;
}
}
+ @GuardedBy("mLock")
private boolean updateDiscoveryPreferenceIfNeededLocked() {
RouteDiscoveryPreference newDiscoveryPreference = new RouteDiscoveryPreference.Builder(
mRouteCallbackRecords.stream().map(record -> record.mPreference).collect(
Collectors.toList())).build();
+
if (Objects.equals(mDiscoveryPreference, newDiscoveryPreference)) {
return false;
}
mDiscoveryPreference = newDiscoveryPreference;
- mShouldUpdateRoutes = true;
+ updateFilteredRoutesLocked();
return true;
}
/**
- * Gets the list of all discovered routes.
- * This list includes the routes that are not related to the client app.
- * <p>
- * This will return an empty list for non-system media routers.
+ * Gets the list of all discovered routes. This list includes the routes that are not related to
+ * the client app.
+ *
+ * <p>This will return an empty list for non-system media routers.
*
* @hide
*/
@@ -464,25 +466,19 @@ public final class MediaRouter2 {
}
/**
- * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
- * known to the media router.
- * <p>
- * Please note that the list can be changed before callbacks are invoked.
- * </p>
+ * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently known to the media
+ * router.
+ *
+ * <p>Please note that the list can be changed before callbacks are invoked.
+ *
* @return the list of routes that contains at least one of the route features in discovery
- * preferences registered by the application
+ * preferences registered by the application
*/
@NonNull
public List<MediaRoute2Info> getRoutes() {
synchronized (mLock) {
- if (mShouldUpdateRoutes) {
- mShouldUpdateRoutes = false;
-
- mFilteredRoutes = Collections.unmodifiableList(
- filterRoutes(List.copyOf(mRoutes.values()), mDiscoveryPreference));
- }
+ return mFilteredRoutes;
}
- return mFilteredRoutes;
}
/**
@@ -493,8 +489,8 @@ public final class MediaRouter2 {
* @param callback the callback to register
* @see #unregisterTransferCallback
*/
- public void registerTransferCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull TransferCallback callback) {
+ public void registerTransferCallback(
+ @NonNull @CallbackExecutor Executor executor, @NonNull TransferCallback callback) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(callback, "callback must not be null");
@@ -522,12 +518,13 @@ public final class MediaRouter2 {
}
/**
- * Registers a {@link ControllerCallback}.
- * If you register the same callback twice or more, it will be ignored.
+ * Registers a {@link ControllerCallback}. If you register the same callback twice or more, it
+ * will be ignored.
+ *
* @see #unregisterControllerCallback(ControllerCallback)
*/
- public void registerControllerCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull ControllerCallback callback) {
+ public void registerControllerCallback(
+ @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(callback, "callback must not be null");
@@ -539,12 +536,12 @@ public final class MediaRouter2 {
}
/**
- * Unregisters a {@link ControllerCallback}. The callback will no longer receive
- * events. If the callback has not been added or been removed already, it is ignored.
+ * Unregisters a {@link ControllerCallback}. The callback will no longer receive events.
+ * If the callback has not been added or been removed already, it is ignored.
+ *
* @see #registerControllerCallback(Executor, ControllerCallback)
*/
- public void unregisterControllerCallback(
- @NonNull ControllerCallback callback) {
+ public void unregisterControllerCallback(@NonNull ControllerCallback callback) {
Objects.requireNonNull(callback, "callback must not be null");
if (!mControllerCallbackRecords.remove(new ControllerCallbackRecord(null, callback))) {
@@ -559,7 +556,7 @@ public final class MediaRouter2 {
* {@link #transferTo(MediaRoute2Info)}.
*
* @param listener A listener to send optional app-specific hints when creating a controller.
- * {@code null} for unset.
+ * {@code null} for unset.
*/
public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) {
if (isSystemRouter()) {
@@ -569,13 +566,11 @@ public final class MediaRouter2 {
}
/**
- * Transfers the current media to the given route.
- * If it's necessary a new {@link RoutingController} is created or it is handled within
- * the current routing controller.
+ * Transfers the current media to the given route. If it's necessary a new
+ * {@link RoutingController} is created or it is handled within the current routing controller.
*
* @param route the route you want to transfer the current media to. Pass {@code null} to
* stop routing of the current media.
- *
* @see TransferCallback#onTransfer
* @see TransferCallback#onTransferFailure
*/
@@ -622,8 +617,8 @@ public final class MediaRouter2 {
/**
* Transfers the media of a routing controller to the given route.
- * <p>
- * This will be no-op for non-system media routers.
+ *
+ * <p>This will be no-op for non-system media routers.
*
* @param controller a routing controller controlling media routing.
* @param route the route you want to transfer the media to.
@@ -638,13 +633,15 @@ public final class MediaRouter2 {
}
}
- void requestCreateController(@NonNull RoutingController controller,
- @NonNull MediaRoute2Info route, long managerRequestId) {
+ void requestCreateController(
+ @NonNull RoutingController controller,
+ @NonNull MediaRoute2Info route,
+ long managerRequestId) {
final int requestId = mNextRequestId.getAndIncrement();
- ControllerCreationRequest request = new ControllerCreationRequest(requestId,
- managerRequestId, route, controller);
+ ControllerCreationRequest request =
+ new ControllerCreationRequest(requestId, managerRequestId, route, controller);
mControllerCreationRequests.add(request);
OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
@@ -663,11 +660,15 @@ public final class MediaRouter2 {
if (stub != null) {
try {
mMediaRouterService.requestCreateSessionWithRouter2(
- stub, requestId, managerRequestId,
- controller.getRoutingSessionInfo(), route, controllerHints);
+ stub,
+ requestId,
+ managerRequestId,
+ controller.getRoutingSessionInfo(),
+ route,
+ controllerHints);
} catch (RemoteException ex) {
Log.e(TAG, "createControllerForTransfer: "
- + "Failed to request for creating a controller.", ex);
+ + "Failed to request for creating a controller.", ex);
mControllerCreationRequests.remove(request);
if (managerRequestId == MANAGER_REQUEST_ID_NONE) {
notifyTransferFailure(route);
@@ -685,11 +686,11 @@ public final class MediaRouter2 {
/**
* Gets a {@link RoutingController} which can control the routes provided by system.
* e.g. Phone speaker, wired headset, Bluetooth, etc.
- * <p>
- * Note: The system controller can't be released. Calling {@link RoutingController#release()}
+ *
+ * <p>Note: The system controller can't be released. Calling {@link RoutingController#release()}
* will be ignored.
- * <p>
- * This method always returns the same instance.
+ *
+ * <p>This method always returns the same instance.
*/
@NonNull
public RoutingController getSystemController() {
@@ -714,8 +715,8 @@ public final class MediaRouter2 {
/**
* Gets the list of currently active {@link RoutingController routing controllers} on which
* media can be played.
- * <p>
- * Note: The list returned here will never be empty. The first element in the list is
+ *
+ * <p>Note: The list returned here will never be empty. The first element in the list is
* always the {@link #getSystemController() system controller}.
*/
@NonNull
@@ -750,8 +751,8 @@ public final class MediaRouter2 {
/**
* Requests a volume change for the route asynchronously.
* It may have no effect if the route is currently not selected.
- * <p>
- * This will be no-op for non-system media routers.
+ *
+ * <p>This will be no-op for non-system media routers.
*
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
* @see #getInstance(Context, String)
@@ -769,55 +770,61 @@ public final class MediaRouter2 {
// If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2()
}
- void syncRoutesOnHandler(List<MediaRoute2Info> currentRoutes,
- RoutingSessionInfo currentSystemSessionInfo) {
+ void syncRoutesOnHandler(
+ List<MediaRoute2Info> currentRoutes, RoutingSessionInfo currentSystemSessionInfo) {
if (currentRoutes == null || currentRoutes.isEmpty() || currentSystemSessionInfo == null) {
Log.e(TAG, "syncRoutesOnHandler: Received wrong data. currentRoutes=" + currentRoutes
+ ", currentSystemSessionInfo=" + currentSystemSessionInfo);
return;
}
+ synchronized (mLock) {
+ mRoutes.clear();
+ for (MediaRoute2Info route : currentRoutes) {
+ mRoutes.put(route.getId(), route);
+ }
+ updateFilteredRoutesLocked();
+ }
+
+ RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo();
+ mSystemController.setRoutingSessionInfo(currentSystemSessionInfo);
+ if (!oldInfo.equals(currentSystemSessionInfo)) {
+ notifyControllerUpdated(mSystemController);
+ }
+ }
+
+ void dispatchFilteredRoutesChangedLocked(List<MediaRoute2Info> newRoutes) {
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
- synchronized (mLock) {
- List<String> currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId)
- .collect(Collectors.toList());
-
- for (String routeId : mRoutes.keySet()) {
- if (!currentRoutesIds.contains(routeId)) {
- // This route is removed while the callback is unregistered.
- MediaRoute2Info route = mRoutes.get(routeId);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
- removedRoutes.add(mRoutes.get(routeId));
- }
- }
- }
+ Set<String> newRouteIds =
+ newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet());
- for (MediaRoute2Info route : currentRoutes) {
- if (mRoutes.containsKey(route.getId())) {
- if (!route.equals(mRoutes.get(route.getId()))) {
- // This route is changed while the callback is unregistered.
- if (route.hasAnyFeatures(
- mDiscoveryPreference.getPreferredFeatures())) {
- changedRoutes.add(route);
- }
- }
- } else {
- // This route is added while the callback is unregistered.
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
- addedRoutes.add(route);
- }
- }
+ for (MediaRoute2Info route : newRoutes) {
+ MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId());
+ if (prevRoute == null) {
+ addedRoutes.add(route);
+ } else if (!prevRoute.equals(route)) {
+ changedRoutes.add(route);
}
+ }
- mRoutes.clear();
- for (MediaRoute2Info route : currentRoutes) {
- mRoutes.put(route.getId(), route);
+ for (int i = 0; i < mPreviousRoutes.size(); i++) {
+ if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) {
+ removedRoutes.add(mPreviousRoutes.valueAt(i));
}
+ }
- mShouldUpdateRoutes = true;
+ // update previous routes
+ for (MediaRoute2Info route : removedRoutes) {
+ mPreviousRoutes.remove(route.getId());
+ }
+ for (MediaRoute2Info route : addedRoutes) {
+ mPreviousRoutes.put(route.getId(), route);
+ }
+ for (MediaRoute2Info route : changedRoutes) {
+ mPreviousRoutes.put(route.getId(), route);
}
if (!addedRoutes.isEmpty()) {
@@ -829,43 +836,23 @@ public final class MediaRouter2 {
if (!changedRoutes.isEmpty()) {
notifyRoutesChanged(changedRoutes);
}
-
- RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo();
- mSystemController.setRoutingSessionInfo(currentSystemSessionInfo);
- if (!oldInfo.equals(currentSystemSessionInfo)) {
- notifyControllerUpdated(mSystemController);
- }
}
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> addedRoutes = new ArrayList<>();
synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
- addedRoutes.add(route);
- }
}
- mShouldUpdateRoutes = true;
- }
- if (!addedRoutes.isEmpty()) {
- notifyRoutesAdded(addedRoutes);
+ updateFilteredRoutesLocked();
}
}
void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> removedRoutes = new ArrayList<>();
synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.remove(route.getId());
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
- removedRoutes.add(route);
- }
}
- mShouldUpdateRoutes = true;
- }
- if (!removedRoutes.isEmpty()) {
- notifyRoutesRemoved(removedRoutes);
+ updateFilteredRoutesLocked();
}
}
@@ -874,23 +861,27 @@ public final class MediaRouter2 {
synchronized (mLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
- changedRoutes.add(route);
- }
}
- mShouldUpdateRoutes = true;
- }
- if (!changedRoutes.isEmpty()) {
- notifyRoutesChanged(changedRoutes);
+ updateFilteredRoutesLocked();
}
}
+ /** Updates filtered routes and dispatch callbacks */
+ @GuardedBy("mLock")
+ void updateFilteredRoutesLocked() {
+ mFilteredRoutes =
+ Collections.unmodifiableList(
+ filterRoutesWithCompositePreferenceLocked(List.copyOf(mRoutes.values())));
+ mHandler.sendMessage(
+ obtainMessage(MediaRouter2::dispatchFilteredRoutesChangedLocked,
+ this, mFilteredRoutes));
+ }
+
/**
- * Creates a controller and calls the {@link TransferCallback#onTransfer}.
- * If the controller creation has failed, then it calls
- * {@link TransferCallback#onTransferFailure}.
- * <p>
- * Pass {@code null} to sessionInfo for the failure case.
+ * Creates a controller and calls the {@link TransferCallback#onTransfer}. If the controller
+ * creation has failed, then it calls {@link TransferCallback#onTransferFailure}.
+ *
+ * <p>Pass {@code null} to sessionInfo for the failure case.
*/
void createControllerOnHandler(int requestId, @Nullable RoutingSessionInfo sessionInfo) {
ControllerCreationRequest matchingRequest = null;
@@ -913,12 +904,15 @@ public final class MediaRouter2 {
if (sessionInfo == null) {
notifyTransferFailure(requestedRoute);
return;
- } else if (!TextUtils.equals(requestedRoute.getProviderId(),
- sessionInfo.getProviderId())) {
- Log.w(TAG, "The session's provider ID does not match the requested route's. "
- + "(requested route's providerId=" + requestedRoute.getProviderId()
- + ", actual providerId=" + sessionInfo.getProviderId()
- + ")");
+ } else if (!TextUtils.equals(requestedRoute.getProviderId(), sessionInfo.getProviderId())) {
+ Log.w(
+ TAG,
+ "The session's provider ID does not match the requested route's. "
+ + "(requested route's providerId="
+ + requestedRoute.getProviderId()
+ + ", actual providerId="
+ + sessionInfo.getProviderId()
+ + ")");
notifyTransferFailure(requestedRoute);
return;
}
@@ -927,9 +921,12 @@ public final class MediaRouter2 {
// When the old controller is released before transferred, treat it as a failure.
// This could also happen when transfer is requested twice or more.
if (!oldController.scheduleRelease()) {
- Log.w(TAG, "createControllerOnHandler: "
- + "Ignoring controller creation for released old controller. "
- + "oldController=" + oldController);
+ Log.w(
+ TAG,
+ "createControllerOnHandler: "
+ + "Ignoring controller creation for released old controller. "
+ + "oldController="
+ + oldController);
if (!sessionInfo.isSystemSession()) {
new RoutingController(sessionInfo).release();
}
@@ -971,15 +968,21 @@ public final class MediaRouter2 {
}
if (matchingController == null) {
- Log.w(TAG, "updateControllerOnHandler: Matching controller not found. uniqueSessionId="
- + sessionInfo.getId());
+ Log.w(
+ TAG,
+ "updateControllerOnHandler: Matching controller not found. uniqueSessionId="
+ + sessionInfo.getId());
return;
}
RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
- Log.w(TAG, "updateControllerOnHandler: Provider IDs are not matched. old="
- + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
+ Log.w(
+ TAG,
+ "updateControllerOnHandler: Provider IDs are not matched. old="
+ + oldInfo.getProviderId()
+ + ", new="
+ + sessionInfo.getProviderId());
return;
}
@@ -1000,24 +1003,31 @@ public final class MediaRouter2 {
if (matchingController == null) {
if (DEBUG) {
- Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. "
- + "uniqueSessionId=" + sessionInfo.getId());
+ Log.d(
+ TAG,
+ "releaseControllerOnHandler: Matching controller not found. "
+ + "uniqueSessionId="
+ + sessionInfo.getId());
}
return;
}
RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
- Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old="
- + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
+ Log.w(
+ TAG,
+ "releaseControllerOnHandler: Provider IDs are not matched. old="
+ + oldInfo.getProviderId()
+ + ", new="
+ + sessionInfo.getProviderId());
return;
}
matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
- void onRequestCreateControllerByManagerOnHandler(RoutingSessionInfo oldSession,
- MediaRoute2Info route, long managerRequestId) {
+ void onRequestCreateControllerByManagerOnHandler(
+ RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
RoutingController controller;
if (oldSession.isSystemSession()) {
controller = getSystemController();
@@ -1033,17 +1043,17 @@ public final class MediaRouter2 {
}
/**
- * Returns whether this router is created with {@link #getInstance(Context, String)}.
- * This kind of router can control the target app's media routing.
+ * Returns whether this router is created with {@link #getInstance(Context, String)}. This kind
+ * of router can control the target app's media routing.
*/
private boolean isSystemRouter() {
return mClientPackageName != null;
}
/**
- * Returns a {@link RoutingSessionInfo} which has the client package name.
- * The client package name is set only when the given sessionInfo doesn't have it.
- * Should only used for system media routers.
+ * Returns a {@link RoutingSessionInfo} which has the client package name. The client package
+ * name is set only when the given sessionInfo doesn't have it. Should only used for system
+ * media routers.
*/
private RoutingSessionInfo ensureClientPackageNameForSystemSession(
@NonNull RoutingSessionInfo sessionInfo) {
@@ -1057,41 +1067,44 @@ public final class MediaRouter2 {
.build();
}
- private List<MediaRoute2Info> getSortedRoutes(List<MediaRoute2Info> routes,
- RouteDiscoveryPreference preference) {
- if (!preference.shouldRemoveDuplicates()) {
+ private List<MediaRoute2Info> getSortedRoutes(
+ List<MediaRoute2Info> routes, List<String> packageOrder) {
+ if (packageOrder.isEmpty()) {
return routes;
}
Map<String, Integer> packagePriority = new ArrayMap<>();
- int count = preference.getDeduplicationPackageOrder().size();
+ int count = packageOrder.size();
for (int i = 0; i < count; i++) {
// the last package will have 1 as the priority
- packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i);
+ packagePriority.put(packageOrder.get(i), count - i);
}
ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes);
// take the negative for descending order
- sortedRoutes.sort(Comparator.comparingInt(
- r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
+ sortedRoutes.sort(
+ Comparator.comparingInt(r -> -packagePriority.getOrDefault(r.getPackageName(), 0)));
return sortedRoutes;
}
- private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
- RouteDiscoveryPreference discoveryPreference) {
+ @GuardedBy("mLock")
+ private List<MediaRoute2Info> filterRoutesWithCompositePreferenceLocked(
+ List<MediaRoute2Info> routes) {
Set<String> deduplicationIdSet = new ArraySet<>();
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
- for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) {
- if (!route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
+ for (MediaRoute2Info route :
+ getSortedRoutes(routes, mDiscoveryPreference.getDeduplicationPackageOrder())) {
+ if (!route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
continue;
}
- if (!discoveryPreference.getAllowedPackages().isEmpty()
+ if (!mDiscoveryPreference.getAllowedPackages().isEmpty()
&& (route.getPackageName() == null
- || !discoveryPreference.getAllowedPackages()
- .contains(route.getPackageName()))) {
+ || !mDiscoveryPreference
+ .getAllowedPackages()
+ .contains(route.getPackageName()))) {
continue;
}
- if (discoveryPreference.shouldRemoveDuplicates()) {
+ if (mDiscoveryPreference.shouldRemoveDuplicates()) {
if (!Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) {
continue;
}
@@ -1102,6 +1115,25 @@ public final class MediaRouter2 {
return filteredRoutes;
}
+ private List<MediaRoute2Info> filterRoutesWithIndividualPreference(
+ List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryPreference) {
+ List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
+ for (MediaRoute2Info route : routes) {
+ if (!route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) {
+ continue;
+ }
+ if (!discoveryPreference.getAllowedPackages().isEmpty()
+ && (route.getPackageName() == null
+ || !discoveryPreference
+ .getAllowedPackages()
+ .contains(route.getPackageName()))) {
+ continue;
+ }
+ filteredRoutes.add(route);
+ }
+ return filteredRoutes;
+ }
+
private void updateAllRoutesFromManager() {
if (!isSystemRouter()) {
return;
@@ -1111,23 +1143,24 @@ public final class MediaRouter2 {
for (MediaRoute2Info route : sManager.getAllRoutes()) {
mRoutes.put(route.getId(), route);
}
- mShouldUpdateRoutes = true;
+ updateFilteredRoutesLocked();
}
}
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
+ for (RouteCallbackRecord record : mRouteCallbackRecords) {
+ List<MediaRoute2Info> filteredRoutes =
+ filterRoutesWithIndividualPreference(routes, record.mPreference);
if (!filteredRoutes.isEmpty()) {
- record.mExecutor.execute(
- () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
+ record.mExecutor.execute(() -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
}
}
}
private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
+ for (RouteCallbackRecord record : mRouteCallbackRecords) {
+ List<MediaRoute2Info> filteredRoutes =
+ filterRoutesWithIndividualPreference(routes, record.mPreference);
if (!filteredRoutes.isEmpty()) {
record.mExecutor.execute(
() -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
@@ -1136,8 +1169,9 @@ public final class MediaRouter2 {
}
private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference);
+ for (RouteCallbackRecord record : mRouteCallbackRecords) {
+ List<MediaRoute2Info> filteredRoutes =
+ filterRoutesWithIndividualPreference(routes, record.mPreference);
if (!filteredRoutes.isEmpty()) {
record.mExecutor.execute(
() -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
@@ -1146,46 +1180,42 @@ public final class MediaRouter2 {
}
private void notifyPreferredFeaturesChanged(List<String> features) {
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
+ for (RouteCallbackRecord record : mRouteCallbackRecords) {
record.mExecutor.execute(
() -> record.mRouteCallback.onPreferredFeaturesChanged(features));
}
}
private void notifyTransfer(RoutingController oldController, RoutingController newController) {
- for (TransferCallbackRecord record: mTransferCallbackRecords) {
+ for (TransferCallbackRecord record : mTransferCallbackRecords) {
record.mExecutor.execute(
() -> record.mTransferCallback.onTransfer(oldController, newController));
}
}
private void notifyTransferFailure(MediaRoute2Info route) {
- for (TransferCallbackRecord record: mTransferCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mTransferCallback.onTransferFailure(route));
+ for (TransferCallbackRecord record : mTransferCallbackRecords) {
+ record.mExecutor.execute(() -> record.mTransferCallback.onTransferFailure(route));
}
}
private void notifyStop(RoutingController controller) {
- for (TransferCallbackRecord record: mTransferCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mTransferCallback.onStop(controller));
+ for (TransferCallbackRecord record : mTransferCallbackRecords) {
+ record.mExecutor.execute(() -> record.mTransferCallback.onStop(controller));
}
}
private void notifyControllerUpdated(RoutingController controller) {
- for (ControllerCallbackRecord record: mControllerCallbackRecords) {
+ for (ControllerCallbackRecord record : mControllerCallbackRecords) {
record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller));
}
}
- /**
- * Callback for receiving events about media route discovery.
- */
+ /** Callback for receiving events about media route discovery. */
public abstract static class RouteCallback {
/**
- * Called when routes are added. Whenever you registers a callback, this will
- * be invoked with known routes.
+ * Called when routes are added. Whenever you registers a callback, this will be invoked
+ * with known routes.
*
* @param routes the list of routes that have been added. It's never empty.
*/
@@ -1199,17 +1229,17 @@ public final class MediaRouter2 {
public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
/**
- * Called when routes are changed. For example, it is called when the route's name
- * or volume have been changed.
+ * Called when routes are changed. For example, it is called when the route's name or volume
+ * have been changed.
*
* @param routes the list of routes that have been changed. It's never empty.
*/
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
/**
- * Called when the client app's preferred features are changed.
- * When this is called, it is recommended to {@link #getRoutes()} to get the routes
- * that are currently available to the app.
+ * Called when the client app's preferred features are changed. When this is called, it is
+ * recommended to {@link #getRoutes()} to get the routes that are currently available to the
+ * app.
*
* @param preferredFeatures the new preferred features set by the application
* @hide
@@ -1218,26 +1248,25 @@ public final class MediaRouter2 {
public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {}
}
- /**
- * Callback for receiving events on media transfer.
- */
+ /** Callback for receiving events on media transfer. */
public abstract static class TransferCallback {
/**
- * Called when a media is transferred between two different routing controllers.
- * This can happen by calling {@link #transferTo(MediaRoute2Info)}.
- * <p> Override this to start playback with {@code newController}. You may want to get
- * the status of the media that is being played with {@code oldController} and resume it
- * continuously with {@code newController}.
- * After this is called, any callbacks with {@code oldController} will not be invoked
- * unless {@code oldController} is the {@link #getSystemController() system controller}.
- * You need to {@link RoutingController#release() release} {@code oldController} before
- * playing the media with {@code newController}.
+ * Called when a media is transferred between two different routing controllers. This can
+ * happen by calling {@link #transferTo(MediaRoute2Info)}.
+ *
+ * <p>Override this to start playback with {@code newController}. You may want to get the
+ * status of the media that is being played with {@code oldController} and resume it
+ * continuously with {@code newController}. After this is called, any callbacks with {@code
+ * oldController} will not be invoked unless {@code oldController} is the {@link
+ * #getSystemController() system controller}. You need to {@link RoutingController#release()
+ * release} {@code oldController} before playing the media with {@code newController}.
*
* @param oldController the previous controller that controlled routing
* @param newController the new controller to control routing
* @see #transferTo(MediaRoute2Info)
*/
- public void onTransfer(@NonNull RoutingController oldController,
+ public void onTransfer(
+ @NonNull RoutingController oldController,
@NonNull RoutingController newController) {}
/**
@@ -1248,61 +1277,58 @@ public final class MediaRouter2 {
public void onTransferFailure(@NonNull MediaRoute2Info requestedRoute) {}
/**
- * Called when a media routing stops. It can be stopped by a user or a provider.
- * App should not continue playing media locally when this method is called.
- * The {@code controller} is released before this method is called.
+ * Called when a media routing stops. It can be stopped by a user or a provider. App should
+ * not continue playing media locally when this method is called. The {@code controller} is
+ * released before this method is called.
*
* @param controller the controller that controlled the stopped media routing
*/
- public void onStop(@NonNull RoutingController controller) { }
+ public void onStop(@NonNull RoutingController controller) {}
}
/**
- * A listener interface to send optional app-specific hints when creating a
- * {@link RoutingController}.
+ * A listener interface to send optional app-specific hints when creating a {@link
+ * RoutingController}.
*/
public interface OnGetControllerHintsListener {
/**
- * Called when the {@link MediaRouter2} or the system is about to request
- * a media route provider service to create a controller with the given route.
- * The {@link Bundle} returned here will be sent to media route provider service as a hint.
- * <p>
- * Since controller creation can be requested by the {@link MediaRouter2} and the system,
- * set the listener as soon as possible after acquiring {@link MediaRouter2} instance.
- * The method will be called on the same thread that calls
- * {@link #transferTo(MediaRoute2Info)} or the main thread if it is requested by the system.
+ * Called when the {@link MediaRouter2} or the system is about to request a media route
+ * provider service to create a controller with the given route. The {@link Bundle} returned
+ * here will be sent to media route provider service as a hint.
+ *
+ * <p>Since controller creation can be requested by the {@link MediaRouter2} and the system,
+ * set the listener as soon as possible after acquiring {@link MediaRouter2} instance. The
+ * method will be called on the same thread that calls {@link #transferTo(MediaRoute2Info)}
+ * or the main thread if it is requested by the system.
*
* @param route the route to create a controller with
- * @return An optional bundle of app-specific arguments to send to the provider,
- * or {@code null} if none. The contents of this bundle may affect the result of
- * controller creation.
+ * @return An optional bundle of app-specific arguments to send to the provider, or {@code
+ * null} if none. The contents of this bundle may affect the result of controller
+ * creation.
* @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle)
*/
@Nullable
Bundle onGetControllerHints(@NonNull MediaRoute2Info route);
}
- /**
- * Callback for receiving {@link RoutingController} updates.
- */
+ /** Callback for receiving {@link RoutingController} updates. */
public abstract static class ControllerCallback {
/**
- * Called when a controller is updated. (e.g., when the selected routes of the
- * controller is changed or when the volume of the controller is changed.)
+ * Called when a controller is updated. (e.g., when the selected routes of the controller is
+ * changed or when the volume of the controller is changed.)
*
- * @param controller the updated controller. It may be the
- * {@link #getSystemController() system controller}.
+ * @param controller the updated controller. It may be the {@link #getSystemController()
+ * system controller}.
* @see #getSystemController()
*/
- public void onControllerUpdated(@NonNull RoutingController controller) { }
+ public void onControllerUpdated(@NonNull RoutingController controller) {}
}
/**
- * A class to control media routing session in media route provider.
- * For example, selecting/deselecting/transferring to routes of a session can be done through
- * this. Instances are created when
- * {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is called,
- * which is invoked after {@link #transferTo(MediaRoute2Info)} is called.
+ * A class to control media routing session in media route provider. For example,
+ * selecting/deselecting/transferring to routes of a session can be done through this. Instances
+ * are created when {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is
+ * called, which is invoked after {@link #transferTo(MediaRoute2Info)} is called.
*/
public class RoutingController {
private final Object mControllerLock = new Object();
@@ -1339,8 +1365,8 @@ public final class MediaRouter2 {
}
/**
- * Gets the original session ID set by
- * {@link RoutingSessionInfo.Builder#Builder(String, String)}.
+ * Gets the original session ID set by {@link RoutingSessionInfo.Builder#Builder(String,
+ * String)}.
*
* @hide
*/
@@ -1353,8 +1379,8 @@ public final class MediaRouter2 {
}
/**
- * Gets the control hints used to control routing session if available.
- * It is set by the media route provider.
+ * Gets the control hints used to control routing session if available. It is set by the
+ * media route provider.
*/
@Nullable
public Bundle getControlHints() {
@@ -1401,11 +1427,12 @@ public final class MediaRouter2 {
/**
* Gets the information about how volume is handled on the session.
- * <p>Please note that you may not control the volume of the session even when
- * you can control the volume of each selected route in the session.
*
- * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
- * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
+ * <p>Please note that you may not control the volume of the session even when you can
+ * control the volume of each selected route in the session.
+ *
+ * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or {@link
+ * MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
*/
@MediaRoute2Info.PlaybackVolume
public int getVolumeHandling() {
@@ -1414,9 +1441,7 @@ public final class MediaRouter2 {
}
}
- /**
- * Gets the maximum volume of the session.
- */
+ /** Gets the maximum volume of the session. */
public int getVolumeMax() {
synchronized (mControllerLock) {
return mSessionInfo.getVolumeMax();
@@ -1425,11 +1450,10 @@ public final class MediaRouter2 {
/**
* Gets the current volume of the session.
- * <p>
- * When it's available, it represents the volume of routing session, which is a group
- * of selected routes. Use {@link MediaRoute2Info#getVolume()}
- * to get the volume of a route,
- * </p>
+ *
+ * <p>When it's available, it represents the volume of routing session, which is a group of
+ * selected routes. Use {@link MediaRoute2Info#getVolume()} to get the volume of a route,
+ *
* @see MediaRoute2Info#getVolume()
*/
public int getVolume() {
@@ -1439,9 +1463,9 @@ public final class MediaRouter2 {
}
/**
- * Returns true if this controller is released, false otherwise.
- * If it is released, then all other getters from this instance may return invalid values.
- * Also, any operations to this instance will be ignored once released.
+ * Returns true if this controller is released, false otherwise. If it is released, then all
+ * other getters from this instance may return invalid values. Also, any operations to this
+ * instance will be ignored once released.
*
* @see #release
*/
@@ -1454,14 +1478,16 @@ public final class MediaRouter2 {
/**
* Selects a route for the remote session. After a route is selected, the media is expected
* to be played to the all the selected routes. This is different from {@link
- * MediaRouter2#transferTo(MediaRoute2Info)} transferring to a route},
- * where the media is expected to 'move' from one route to another.
- * <p>
- * The given route must satisfy all of the following conditions:
+ * MediaRouter2#transferTo(MediaRoute2Info)} transferring to a route}, where the media is
+ * expected to 'move' from one route to another.
+ *
+ * <p>The given route must satisfy all of the following conditions:
+ *
* <ul>
- * <li>It should not be included in {@link #getSelectedRoutes()}</li>
- * <li>It should be included in {@link #getSelectableRoutes()}</li>
+ * <li>It should not be included in {@link #getSelectedRoutes()}
+ * <li>It should be included in {@link #getSelectableRoutes()}
* </ul>
+ *
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #deselectRoute(MediaRoute2Info)
@@ -1509,12 +1535,14 @@ public final class MediaRouter2 {
/**
* Deselects a route from the remote session. After a route is deselected, the media is
* expected to be stopped on the deselected route.
- * <p>
- * The given route must satisfy all of the following conditions:
+ *
+ * <p>The given route must satisfy all of the following conditions:
+ *
* <ul>
- * <li>It should be included in {@link #getSelectedRoutes()}</li>
- * <li>It should be included in {@link #getDeselectableRoutes()}</li>
+ * <li>It should be included in {@link #getSelectedRoutes()}
+ * <li>It should be included in {@link #getDeselectableRoutes()}
* </ul>
+ *
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #getSelectedRoutes()
@@ -1559,8 +1587,8 @@ public final class MediaRouter2 {
}
/**
- * Transfers to a given route for the remote session. The given route must be included
- * in {@link RoutingSessionInfo#getTransferableRoutes()}.
+ * Transfers to a given route for the remote session. The given route must be included in
+ * {@link RoutingSessionInfo#getTransferableRoutes()}.
*
* @see RoutingSessionInfo#getSelectedRoutes()
* @see RoutingSessionInfo#getTransferableRoutes()
@@ -1597,7 +1625,7 @@ public final class MediaRouter2 {
* Requests a volume change for the remote session asynchronously.
*
* @param volume The new volume value between 0 and {@link RoutingController#getVolumeMax}
- * (inclusive).
+ * (inclusive).
* @see #getVolume()
*/
public void setVolume(int volume) {
@@ -1634,9 +1662,9 @@ public final class MediaRouter2 {
}
/**
- * Releases this controller and the corresponding session.
- * Any operations on this controller after calling this method will be ignored.
- * The devices that are playing media will stop playing it.
+ * Releases this controller and the corresponding session. Any operations on this controller
+ * after calling this method will be ignored. The devices that are playing media will stop
+ * playing it.
*/
public void release() {
releaseInternal(/* shouldReleaseSession= */ true);
@@ -1644,8 +1672,9 @@ public final class MediaRouter2 {
/**
* Schedules release of the controller.
+ *
* @return {@code true} if it's successfully scheduled, {@code false} if it's already
- * scheduled to be released or released.
+ * scheduled to be released or released.
*/
boolean scheduleRelease() {
synchronized (mControllerLock) {
@@ -1701,11 +1730,15 @@ public final class MediaRouter2 {
}
if (shouldNotifyStop) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this,
- RoutingController.this));
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::notifyStop,
+ MediaRouter2.this,
+ RoutingController.this));
}
- if (mRouteCallbackRecords.isEmpty() && mNonSystemRoutingControllers.isEmpty()
+ if (mRouteCallbackRecords.isEmpty()
+ && mNonSystemRoutingControllers.isEmpty()
&& mStub != null) {
try {
mMediaRouterService.unregisterRouter2(mStub);
@@ -1720,26 +1753,34 @@ public final class MediaRouter2 {
@Override
public String toString() {
// To prevent logging spam, we only print the ID of each route.
- List<String> selectedRoutes = getSelectedRoutes().stream()
- .map(MediaRoute2Info::getId).collect(Collectors.toList());
- List<String> selectableRoutes = getSelectableRoutes().stream()
- .map(MediaRoute2Info::getId).collect(Collectors.toList());
- List<String> deselectableRoutes = getDeselectableRoutes().stream()
- .map(MediaRoute2Info::getId).collect(Collectors.toList());
-
- StringBuilder result = new StringBuilder()
- .append("RoutingController{ ")
- .append("id=").append(getId())
- .append(", selectedRoutes={")
- .append(selectedRoutes)
- .append("}")
- .append(", selectableRoutes={")
- .append(selectableRoutes)
- .append("}")
- .append(", deselectableRoutes={")
- .append(deselectableRoutes)
- .append("}")
- .append(" }");
+ List<String> selectedRoutes =
+ getSelectedRoutes().stream()
+ .map(MediaRoute2Info::getId)
+ .collect(Collectors.toList());
+ List<String> selectableRoutes =
+ getSelectableRoutes().stream()
+ .map(MediaRoute2Info::getId)
+ .collect(Collectors.toList());
+ List<String> deselectableRoutes =
+ getDeselectableRoutes().stream()
+ .map(MediaRoute2Info::getId)
+ .collect(Collectors.toList());
+
+ StringBuilder result =
+ new StringBuilder()
+ .append("RoutingController{ ")
+ .append("id=")
+ .append(getId())
+ .append(", selectedRoutes={")
+ .append(selectedRoutes)
+ .append("}")
+ .append(", selectableRoutes={")
+ .append(selectableRoutes)
+ .append("}")
+ .append(", deselectableRoutes={")
+ .append(deselectableRoutes)
+ .append("}")
+ .append(" }");
return result.toString();
}
@@ -1764,7 +1805,8 @@ public final class MediaRouter2 {
}
synchronized (mLock) {
- return routeIds.stream().map(mRoutes::get)
+ return routeIds.stream()
+ .map(mRoutes::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@@ -1799,7 +1841,9 @@ public final class MediaRouter2 {
public final RouteCallback mRouteCallback;
public final RouteDiscoveryPreference mPreference;
- RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
+ RouteCallbackRecord(
+ @Nullable Executor executor,
+ @NonNull RouteCallback routeCallback,
@Nullable RouteDiscoveryPreference preference) {
mRouteCallback = routeCallback;
mExecutor = executor;
@@ -1827,8 +1871,8 @@ public final class MediaRouter2 {
public final Executor mExecutor;
public final TransferCallback mTransferCallback;
- TransferCallbackRecord(@NonNull Executor executor,
- @NonNull TransferCallback transferCallback) {
+ TransferCallbackRecord(
+ @NonNull Executor executor, @NonNull TransferCallback transferCallback) {
mTransferCallback = transferCallback;
mExecutor = executor;
}
@@ -1854,8 +1898,8 @@ public final class MediaRouter2 {
public final Executor mExecutor;
public final ControllerCallback mCallback;
- ControllerCallbackRecord(@Nullable Executor executor,
- @NonNull ControllerCallback callback) {
+ ControllerCallbackRecord(
+ @Nullable Executor executor, @NonNull ControllerCallback callback) {
mCallback = callback;
mExecutor = executor;
}
@@ -1883,66 +1927,87 @@ public final class MediaRouter2 {
public final MediaRoute2Info mRoute;
public final RoutingController mOldController;
- ControllerCreationRequest(int requestId, long managerRequestId,
- @NonNull MediaRoute2Info route, @NonNull RoutingController oldController) {
+ ControllerCreationRequest(
+ int requestId,
+ long managerRequestId,
+ @NonNull MediaRoute2Info route,
+ @NonNull RoutingController oldController) {
mRequestId = requestId;
mManagerRequestId = managerRequestId;
mRoute = Objects.requireNonNull(route, "route must not be null");
- mOldController = Objects.requireNonNull(oldController,
- "oldController must not be null");
+ mOldController =
+ Objects.requireNonNull(oldController, "oldController must not be null");
}
}
class MediaRouter2Stub extends IMediaRouter2.Stub {
@Override
- public void notifyRouterRegistered(List<MediaRoute2Info> currentRoutes,
- RoutingSessionInfo currentSystemSessionInfo) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::syncRoutesOnHandler,
- MediaRouter2.this, currentRoutes, currentSystemSessionInfo));
+ public void notifyRouterRegistered(
+ List<MediaRoute2Info> currentRoutes, RoutingSessionInfo currentSystemSessionInfo) {
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::syncRoutesOnHandler,
+ MediaRouter2.this,
+ currentRoutes,
+ currentSystemSessionInfo));
}
@Override
public void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler,
- MediaRouter2.this, routes));
+ mHandler.sendMessage(
+ obtainMessage(MediaRouter2::addRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
public void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::removeRoutesOnHandler,
- MediaRouter2.this, routes));
+ mHandler.sendMessage(
+ obtainMessage(MediaRouter2::removeRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
public void notifyRoutesChanged(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::changeRoutesOnHandler,
- MediaRouter2.this, routes));
+ mHandler.sendMessage(
+ obtainMessage(MediaRouter2::changeRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
public void notifySessionCreated(int requestId, @Nullable RoutingSessionInfo sessionInfo) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
- MediaRouter2.this, requestId, sessionInfo));
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::createControllerOnHandler,
+ MediaRouter2.this,
+ requestId,
+ sessionInfo));
}
@Override
public void notifySessionInfoChanged(@Nullable RoutingSessionInfo sessionInfo) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::updateControllerOnHandler,
- MediaRouter2.this, sessionInfo));
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::updateControllerOnHandler,
+ MediaRouter2.this,
+ sessionInfo));
}
@Override
public void notifySessionReleased(RoutingSessionInfo sessionInfo) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
- MediaRouter2.this, sessionInfo));
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::releaseControllerOnHandler,
+ MediaRouter2.this,
+ sessionInfo));
}
@Override
- public void requestCreateSessionByManager(long managerRequestId,
- RoutingSessionInfo oldSession, MediaRoute2Info route) {
- mHandler.sendMessage(obtainMessage(
- MediaRouter2::onRequestCreateControllerByManagerOnHandler,
- MediaRouter2.this, oldSession, route, managerRequestId));
+ public void requestCreateSessionByManager(
+ long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::onRequestCreateControllerByManagerOnHandler,
+ MediaRouter2.this,
+ oldSession,
+ route,
+ managerRequestId));
}
}
@@ -1952,57 +2017,21 @@ public final class MediaRouter2 {
@Override
public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {
updateAllRoutesFromManager();
-
- List<MediaRoute2Info> filteredRoutes;
- synchronized (mLock) {
- filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
- }
- if (filteredRoutes.isEmpty()) {
- return;
- }
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mRouteCallback.onRoutesAdded(filteredRoutes));
- }
}
@Override
public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {
updateAllRoutesFromManager();
-
- List<MediaRoute2Info> filteredRoutes;
- synchronized (mLock) {
- filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
- }
- if (filteredRoutes.isEmpty()) {
- return;
- }
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes));
- }
}
@Override
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {
updateAllRoutesFromManager();
-
- List<MediaRoute2Info> filteredRoutes;
- synchronized (mLock) {
- filteredRoutes = filterRoutes(routes, mDiscoveryPreference);
- }
- if (filteredRoutes.isEmpty()) {
- return;
- }
- for (RouteCallbackRecord record: mRouteCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mRouteCallback.onRoutesChanged(filteredRoutes));
- }
}
@Override
- public void onTransferred(@NonNull RoutingSessionInfo oldSession,
- @NonNull RoutingSessionInfo newSession) {
+ public void onTransferred(
+ @NonNull RoutingSessionInfo oldSession, @NonNull RoutingSessionInfo newSession) {
if (!oldSession.isSystemSession()
&& !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) {
return;
@@ -2018,7 +2047,6 @@ public final class MediaRouter2 {
return;
}
-
RoutingController oldController;
if (oldSession.isSystemSession()) {
mSystemController.setRoutingSessionInfo(
@@ -2041,8 +2069,8 @@ public final class MediaRouter2 {
}
@Override
- public void onTransferFailed(@NonNull RoutingSessionInfo session,
- @NonNull MediaRoute2Info route) {
+ public void onTransferFailed(
+ @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) {
if (!session.isSystemSession()
&& !TextUtils.equals(mClientPackageName, session.getClientPackageName())) {
return;
@@ -2083,8 +2111,8 @@ public final class MediaRouter2 {
}
@Override
- public void onDiscoveryPreferenceChanged(@NonNull String packageName,
- @NonNull RouteDiscoveryPreference preference) {
+ public void onDiscoveryPreferenceChanged(
+ @NonNull String packageName, @NonNull RouteDiscoveryPreference preference) {
if (!TextUtils.equals(mClientPackageName, packageName)) {
return;
}
diff --git a/media/java/android/media/NearbyDevice.java b/media/java/android/media/NearbyDevice.java
index c203e7b2b88b..dbcc6b716c8f 100644
--- a/media/java/android/media/NearbyDevice.java
+++ b/media/java/android/media/NearbyDevice.java
@@ -22,9 +22,10 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
/**
* A parcelable representing a nearby device that can be used for media transfer.
@@ -35,6 +36,7 @@ import java.lang.annotation.RetentionPolicy;
* <li>a range zone specifying how far away this device is from the device with the media route.
* </li>
* </ul>
+ *
* @hide
*/
@SystemApi
@@ -69,7 +71,7 @@ public final class NearbyDevice implements Parcelable {
*
* @hide
*/
- @IntDef(prefix = { "RANGE_" }, value = {
+ @IntDef(prefix = {"RANGE_"}, value = {
RANGE_UNKNOWN,
RANGE_FAR,
RANGE_LONG,
@@ -77,7 +79,8 @@ public final class NearbyDevice implements Parcelable {
RANGE_WITHIN_REACH
})
@Retention(RetentionPolicy.SOURCE)
- public @interface RangeZone {}
+ public @interface RangeZone {
+ }
/**
* Gets a human-readable string of the range zone.
@@ -102,8 +105,17 @@ public final class NearbyDevice implements Parcelable {
}
}
- @NonNull private final String mMediaRoute2Id;
- @RangeZone private final int mRangeZone;
+ /**
+ * A list stores all the range and list from far to close, used for range comparison.
+ */
+ private static final List<Integer> RANGE_WEIGHT_LIST =
+ Arrays.asList(RANGE_UNKNOWN,
+ RANGE_FAR, RANGE_LONG, RANGE_CLOSE, RANGE_WITHIN_REACH);
+
+ @NonNull
+ private final String mMediaRoute2Id;
+ @RangeZone
+ private final int mRangeZone;
/** Creates a device object with the given ID and range zone. */
public NearbyDevice(@NonNull String mediaRoute2Id, @RangeZone int rangeZone) {
@@ -129,6 +141,22 @@ public final class NearbyDevice implements Parcelable {
}
};
+ /**
+ * Compares two ranges and return result.
+ *
+ * @return 0 means two ranges are the same, -1 means first range is closer, 1 means farther
+ *
+ * @hide
+ */
+ public static int compareRangeZones(@RangeZone int rangeZone, @RangeZone int anotherRangeZone) {
+ if (rangeZone == anotherRangeZone) {
+ return 0;
+ } else {
+ return RANGE_WEIGHT_LIST.indexOf(rangeZone) > RANGE_WEIGHT_LIST.indexOf(
+ anotherRangeZone) ? -1 : 1;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
index e1f535c93d19..6103db001b19 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -131,6 +131,10 @@ public final class TvInteractiveAppInfo implements Parcelable {
dest.writeInt(mTypes);
}
+ /**
+ * Returns a unique ID for this TV interactive app service. The ID is generated from the package
+ * and class name implementing the TV interactive app service.
+ */
@NonNull
public String getId() {
return mId;
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 22725254fef6..9e0a6eb476d3 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -140,8 +140,7 @@ public:
}
}
- void mountObb(const char* rawPath, const char* key, AStorageManager_obbCallbackFunc func,
- void* data) {
+ void mountObb(const char* rawPath, AStorageManager_obbCallbackFunc func, void* data) {
// Resolve path before sending to MountService
char canonicalPath[PATH_MAX];
if (realpath(rawPath, canonicalPath) == NULL) {
@@ -158,9 +157,7 @@ public:
ObbCallback* cb = registerObbCallback(func, data);
String16 rawPath16(rawPath);
String16 canonicalPath16(canonicalPath);
- String16 key16(key);
- mMountService->mountObb(rawPath16, canonicalPath16, key16, mObbActionListener,
- cb->nonce, obbInfo);
+ mMountService->mountObb(rawPath16, canonicalPath16, mObbActionListener, cb->nonce, obbInfo);
}
void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) {
@@ -207,7 +204,11 @@ void AStorageManager_delete(AStorageManager* mgr) {
void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
AStorageManager_obbCallbackFunc cb, void* data) {
- mgr->mountObb(filename, key, cb, data);
+ if (key != nullptr && key[0] != '\0') {
+ ALOGE("mounting encrypted OBBs is no longer supported");
+ return;
+ }
+ mgr->mountObb(filename, cb, data);
}
void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
diff --git a/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml b/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml
new file mode 100644
index 000000000000..125fee6282d5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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="rectangle">
+ <solid android:color="@android:color/transparent" />
+ <corners android:topLeftRadius="16dp" android:topRightRadius="16dp"
+ android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp"/>
+ <stroke
+ android:width="2dp"
+ android:color="@android:color/system_accent1_600" />
+</shape> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml b/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml
new file mode 100644
index 000000000000..7df92bb145cb
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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="rectangle">
+ <solid android:color="@android:color/system_accent1_100"/>
+ <corners android:topLeftRadius="12dp" android:topRightRadius="12dp"
+ android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp"/>
+</shape>
diff --git a/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml b/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml
new file mode 100644
index 000000000000..55e96f6d7512
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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="rectangle">
+ <solid android:color="@android:color/system_accent1_100"/>
+ <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"
+ android:bottomLeftRadius="12dp" android:bottomRightRadius="12dp"/>
+</shape>
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
new file mode 100644
index 000000000000..93a0cba769c6
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M6.2529,18.5H16.2529V17.5H18.2529V21.5C18.2529,22.6 17.3529,23.5 16.2529,23.5H6.2529C5.1529,23.5 4.2529,22.6 4.2529,21.5V3.5C4.2529,2.4 5.1529,1.51 6.2529,1.51L16.2529,1.5C17.3529,1.5 18.2529,2.4 18.2529,3.5V7.5H16.2529V6.5H6.2529V18.5ZM16.2529,3.5H6.2529V4.5H16.2529V3.5ZM6.2529,21.5V20.5H16.2529V21.5H6.2529ZM12.6553,9.4049C12.6553,8.8526 13.103,8.4049 13.6553,8.4049H20.5254C21.0776,8.4049 21.5254,8.8526 21.5254,9.4049V14.6055C21.5254,15.1578 21.0776,15.6055 20.5254,15.6055H14.355L12.6553,17.0871V9.4049Z"
+ android:fillColor="#3C4043"
+ android:fillType="evenOdd"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
new file mode 100644
index 000000000000..4ac4d04b184e
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M4,19V17H6V10Q6,7.925 7.25,6.312Q8.5,4.7 10.5,4.2V3.5Q10.5,2.875 10.938,2.438Q11.375,2 12,2Q12.625,2 13.062,2.438Q13.5,2.875 13.5,3.5V4.2Q15.5,4.7 16.75,6.312Q18,7.925 18,10V17H20V19ZM12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5ZM12,22Q11.175,22 10.588,21.413Q10,20.825 10,20H14Q14,20.825 13.413,21.413Q12.825,22 12,22ZM8,17H16V10Q16,8.35 14.825,7.175Q13.65,6 12,6Q10.35,6 9.175,7.175Q8,8.35 8,10Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
new file mode 100644
index 000000000000..d8b7f59185c8
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M6,17H18L14.25,12L11.25,16L9,13ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19ZM5,5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Q5,19 5,19Q5,19 5,19V5Q5,5 5,5Q5,5 5,5Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index f30dadffa788..9e5b1663ff45 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -23,27 +23,27 @@
<!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
<TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingHorizontal="12dp"
- style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingHorizontal="12dp"
+ style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
<TextView
- android:id="@+id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:gravity="center"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp" />
<RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/device_list"
@@ -51,30 +51,39 @@
android:scrollbars="vertical"
android:layout_height="200dp" />
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/permission_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
</RelativeLayout>
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="end">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="24dp">
<!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
<Button
- android:id="@+id/btn_negative"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_no"
- android:textColor="?android:attr/textColorSecondary" />
+ android:id="@+id/btn_negative"
+ style="@style/NegativeButton"
+ android:text="@string/consent_no" />
+
+ <Button
+ android:id="@+id/btn_positive"
+ style="@style/PositiveButton"
+ android:text="@string/consent_yes" />
<Button
- android:id="@+id/btn_positive"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_yes" />
+ android:id="@+id/btn_negative_multiple_devices"
+ android:layout_marginLeft="170dp"
+ android:layout_marginBottom="10dp"
+ style="@style/NegativeButtonMultipleDevices"
+ android:textColor = "?android:textColorPrimary"
+ android:visibility="gone"
+ android:text="@string/consent_no" />
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
index a1855fdda78d..7c508147e0ac 100644
--- a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -28,45 +28,40 @@
<!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
<TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingHorizontal="12dp"
- style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingHorizontal="12dp"
+ style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
<TextView
- android:id="@+id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:gravity="center"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp" />
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="end">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="24dp">
<!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
<Button
- android:id="@+id/btn_negative"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_no"
- android:textColor="?android:attr/textColorSecondary" />
+ android:id="@+id/btn_negative"
+ style="@style/NegativeButton"
+ android:text="@string/consent_no" />
<Button
- android:id="@+id/btn_positive"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_yes" />
+ android:id="@+id/btn_positive"
+ style="@style/PositiveButton"
+ android:text="@string/consent_yes" />
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
index 8fd1fb0de891..c177039891d2 100644
--- a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
@@ -60,4 +60,4 @@
</LinearLayout>
-</LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index 153fc1f35abe..b732b1bd9ad8 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -25,16 +25,16 @@
<!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
<ImageView
- android:id="@android:id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginRight="12dp"/>
+ android:id="@android:id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginRight="12dp"/>
<TextView
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
new file mode 100644
index 000000000000..b8a0f7938f0b
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -0,0 +1,55 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_item_permission"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="5dp">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginTop="7dp"
+ android:layout_marginEnd="12dp"
+ android:contentDescription="Permission Icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ android:padding="6dp">
+
+ <TextView
+ android:id="@+id/permission_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
+
+ <TextView
+ android:id="@+id/permission_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values-night/themes.xml b/packages/CompanionDeviceManager/res/values-night/themes.xml
new file mode 100644
index 000000000000..6eb16e726321
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values-night/themes.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+
+ <style name="ChooserActivity"
+ parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="*android:windowFixedHeightMajor">100%</item>
+ <item name="*android:windowFixedHeightMinor">100%</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
+
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 9626751679e1..55a199813388 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -38,8 +38,14 @@
<!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
+ <!-- Apps permission will be granted of APP_STREAMING profile [CHAR LIMIT=30] -->
+ <string name="permission_apps">Apps</string>
+
+ <!-- Description of apps permission of APP_STREAMING profile [CHAR LIMIT=NONE] -->
+ <string name="permission_apps_summary">Stream your phone\u2019s apps</string>
+
<!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
- <string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to access this information for your phone</string>
+ <string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone</string>
<!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
<string name="summary_app_streaming" product="default">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected.</string>
@@ -72,6 +78,18 @@
<!-- Description of the privileges the application will get if associated with the companion device of COMPUTER profile (type) [CHAR LIMIT=NONE] -->
<string name="summary_computer"></string>
+ <!-- Notification permission will be granted of COMPUTER profile [CHAR LIMIT=30] -->
+ <string name="permission_notification">Notifications</string>
+
+ <!-- Description of notification permission of COMPUTER profile [CHAR LIMIT=NONE] -->
+ <string name="permission_notification_summary">Can read all notifications, including information like contracts, messages, and photos</string>
+
+ <!-- Storage permission will be granted of COMPUTER profile [CHAR LIMIT=30] -->
+ <string name="permission_storage">Photos and media</string>
+
+ <!-- Description of storage permission of COMPUTER profile [CHAR LIMIT=NONE] -->
+ <string name="permission_storage_summary"></string>
+
<!-- Title of the helper dialog for COMPUTER profile [CHAR LIMIT=30]. -->
<string name="helper_title_computer">Google Play services</string>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index bba45e9ecf0f..4a267db3ceec 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -35,4 +35,34 @@
<item name="android:background">@drawable/helper_ok_button</item>
</style>
+ <style name="NegativeButton"
+ parent="@android:style/Widget.Material.Button.Borderless.Colored">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:background">@drawable/btn_negative_top</item>
+ </style>
+
+ <style name="PositiveButton"
+ parent="@android:style/Widget.Material.Button.Borderless.Colored">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ <item name="android:layout_marginTop">4dp</item>
+ <item name="android:background">@drawable/btn_positive_bottom</item>
+ </style>
+
+ <style name="NegativeButtonMultipleDevices"
+ parent="@android:style/Widget.Material.Button.Colored">
+ <item name="android:layout_width">100dp</item>
+ <item name="android:layout_height">35dp</item>
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:background">@drawable/btn_negative_multiple_devices</item>
+ </style>
+
</resources> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/themes.xml b/packages/CompanionDeviceManager/res/values/themes.xml
index 72404322c25e..e3fc67c50d75 100644
--- a/packages/CompanionDeviceManager/res/values/themes.xml
+++ b/packages/CompanionDeviceManager/res/values/themes.xml
@@ -17,11 +17,10 @@
<resources>
<style name="ChooserActivity"
- parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar">
<item name="*android:windowFixedHeightMajor">100%</item>
<item name="*android:windowFixedHeightMinor">100%</item>
<item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:forceDarkAllowed">true</item>
</style>
</resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 0fba250b200a..0ab126a7dae0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -24,6 +24,9 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_APPS;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_NOTIFICATION;
+import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_STORAGE;
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon;
@@ -61,6 +64,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -114,16 +118,21 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// Present for self-managed association requests and "single-device" regular association
// regular.
private Button mButtonAllow;
- // Present for all associations.
private Button mButtonNotAllow;
+ // Present for multiple devices association requests only.
+ private Button mButtonNotAllowMultipleDevices;
private LinearLayout mAssociationConfirmationDialog;
private RelativeLayout mVendorHeader;
// The recycler view is only shown for multiple-device regular association request, after
// at least one matching device is found.
- private @Nullable RecyclerView mRecyclerView;
- private @Nullable DeviceListAdapter mAdapter;
+ private @Nullable RecyclerView mDeviceListRecyclerView;
+ private @Nullable DeviceListAdapter mDeviceAdapter;
+
+ // The recycler view is only shown for selfManaged association request.
+ private @Nullable RecyclerView mPermissionListRecyclerView;
+ private @Nullable PermissionListAdapter mPermissionListAdapter;
// The flag used to prevent double taps, that may lead to sending several requests for creating
// an association to CDM.
@@ -133,6 +142,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// onActivityResult() after the association is created.
private @Nullable DeviceFilterPair<?> mSelectedDevice;
+ private @Nullable List<Integer> mPermissionTypes;
+
@Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate()");
@@ -242,14 +253,20 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mVendorHeaderName = findViewById(R.id.vendor_header_name);
mVendorHeaderButton = findViewById(R.id.vendor_header_button);
- mRecyclerView = findViewById(R.id.device_list);
- mAdapter = new DeviceListAdapter(this, this::onListItemClick);
+ mDeviceListRecyclerView = findViewById(R.id.device_list);
+ mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick);
+
+ mPermissionListRecyclerView = findViewById(R.id.permission_list);
+ mPermissionListAdapter = new PermissionListAdapter(this);
mButtonAllow = findViewById(R.id.btn_positive);
mButtonNotAllow = findViewById(R.id.btn_negative);
+ mButtonNotAllowMultipleDevices = findViewById(R.id.btn_negative_multiple_devices);
mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
mButtonNotAllow.setOnClickListener(this::onNegativeButtonClick);
+ mButtonNotAllowMultipleDevices.setOnClickListener(this::onNegativeButtonClick);
+
mVendorHeaderButton.setOnClickListener(this::onShowHelperDialog);
if (mRequest.isSelfManaged()) {
@@ -359,7 +376,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
final Drawable vendorIcon;
final CharSequence vendorName;
final Spanned title;
- final Spanned summary;
+
+ mPermissionTypes = new ArrayList<>();
try {
vendorIcon = getVendorHeaderIcon(this, packageName, userId);
@@ -372,33 +390,36 @@ public class CompanionDeviceActivity extends FragmentActivity implements
switch (deviceProfile) {
case DEVICE_PROFILE_APP_STREAMING:
- title = getHtmlFromResources(this, R.string.title_app_streaming, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_app_streaming, appLabel, deviceName);
+ title = getHtmlFromResources(this, R.string.title_app_streaming, deviceName);
+ mPermissionTypes.add(TYPE_APPS);
break;
case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION:
- title = getHtmlFromResources(this, R.string.title_automotive_projection, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_automotive_projection, appLabel, deviceName);
+ title = getHtmlFromResources(
+ this, R.string.title_automotive_projection, deviceName);
break;
case DEVICE_PROFILE_COMPUTER:
- title = getHtmlFromResources(this, R.string.title_computer, appLabel);
- summary = getHtmlFromResources(
- this, R.string.summary_computer, appLabel, deviceName);
+ title = getHtmlFromResources(this, R.string.title_computer, deviceName);
+ mPermissionTypes.add(TYPE_NOTIFICATION);
+ mPermissionTypes.add(TYPE_STORAGE);
break;
default:
throw new RuntimeException("Unsupported profile " + deviceProfile);
}
+ mSummary.setVisibility(View.GONE);
+
+ mPermissionListAdapter.setPermissionType(mPermissionTypes);
+ mPermissionListRecyclerView.setAdapter(mPermissionListAdapter);
+ mPermissionListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
mTitle.setText(title);
- mSummary.setText(summary);
mVendorHeaderImage.setImageDrawable(vendorIcon);
mVendorHeaderName.setText(vendorName);
- mRecyclerView.setVisibility(View.GONE);
+ mDeviceListRecyclerView.setVisibility(View.GONE);
mVendorHeader.setVisibility(View.VISIBLE);
}
@@ -411,7 +432,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements
deviceFilterPairs -> updateSingleDeviceUi(
deviceFilterPairs, deviceProfile, appLabel));
- mRecyclerView.setVisibility(View.GONE);
+ mPermissionListRecyclerView.setVisibility(View.GONE);
+ mDeviceListRecyclerView.setVisibility(View.GONE);
}
private void updateSingleDeviceUi(List<DeviceFilterPair<?>> deviceFilterPairs,
@@ -460,31 +482,33 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mTitle.setText(title);
mSummary.setText(summary);
- mAdapter = new DeviceListAdapter(this, this::onListItemClick);
+ mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick);
// TODO: hide the list and show a spinner until a first device matching device is found.
- mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
+ mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
CompanionDeviceDiscoveryService.getScanResult().observe(
/* lifecycleOwner */ this,
- /* observer */ mAdapter);
+ /* observer */ mDeviceAdapter);
// "Remove" consent button: users would need to click on the list item.
mButtonAllow.setVisibility(View.GONE);
+ mButtonNotAllow.setVisibility(View.GONE);
+ mButtonNotAllowMultipleDevices.setVisibility(View.VISIBLE);
}
private void onListItemClick(int position) {
if (DEBUG) Log.d(TAG, "onListItemClick() " + position);
- final DeviceFilterPair<?> selectedDevice = mAdapter.getItem(position);
+ final DeviceFilterPair<?> selectedDevice = mDeviceAdapter.getItem(position);
if (mSelectedDevice != null) {
if (DEBUG) Log.w(TAG, "Already selected.");
return;
}
// Notify the adapter to highlight the selected item.
- mAdapter.setSelectedPosition(position);
+ mDeviceAdapter.setSelectedPosition(position);
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index e5513b074865..8babd3ade1eb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -15,9 +15,10 @@
*/
package com.android.companiondevicemanager;
+
+import static com.android.companiondevicemanager.Utils.getIcon;
+
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -60,11 +61,11 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde
R.layout.list_item_device, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
if (viewType == TYPE_WIFI) {
- viewHolder.mImageView.setImageDrawable(getIcon(
- com.android.internal.R.drawable.ic_wifi_signal_3));
+ viewHolder.mImageView.setImageDrawable(
+ getIcon(mContext, com.android.internal.R.drawable.ic_wifi_signal_3));
} else {
- viewHolder.mImageView.setImageDrawable(getIcon(
- android.R.drawable.stat_sys_data_bluetooth));
+ viewHolder.mImageView.setImageDrawable(
+ getIcon(mContext, android.R.drawable.stat_sys_data_bluetooth));
}
return viewHolder;
}
@@ -115,12 +116,6 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde
return mDevices.get(position).getDevice() instanceof android.net.wifi.ScanResult;
}
- private Drawable getIcon(int resId) {
- Drawable icon = mContext.getResources().getDrawable(resId, null);
- icon.setTint(Color.DKGRAY);
- return icon;
- }
-
public interface OnItemClickListener {
void onItemClick(int position);
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
new file mode 100644
index 000000000000..895b729ea8c7
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.companiondevicemanager;
+
+import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
+import static com.android.companiondevicemanager.Utils.getIcon;
+
+import static java.util.Collections.unmodifiableMap;
+
+import android.content.Context;
+import android.text.Spanned;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+import java.util.Map;
+
+class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.ViewHolder> {
+ private final Context mContext;
+
+ private List<Integer> mPermissions;
+
+ static final int TYPE_NOTIFICATION = 0;
+ static final int TYPE_STORAGE = 1;
+ static final int TYPE_APPS = 2;
+
+ private static final Map<Integer, Integer> sTitleMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.string.permission_notification);
+ map.put(TYPE_STORAGE, R.string.permission_storage);
+ map.put(TYPE_APPS, R.string.permission_apps);
+ sTitleMap = unmodifiableMap(map);
+ }
+
+ private static final Map<Integer, Integer> sSummaryMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.string.permission_notification_summary);
+ map.put(TYPE_STORAGE, R.string.permission_storage_summary);
+ map.put(TYPE_APPS, R.string.permission_apps_summary);
+ sSummaryMap = unmodifiableMap(map);
+ }
+
+ private static final Map<Integer, Integer> sIconMap;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(TYPE_NOTIFICATION, R.drawable.ic_notifications);
+ map.put(TYPE_STORAGE, R.drawable.ic_storage);
+ map.put(TYPE_APPS, R.drawable.ic_apps);
+ sIconMap = unmodifiableMap(map);
+ }
+
+ PermissionListAdapter(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.list_item_permission, parent, false);
+ ViewHolder viewHolder = new ViewHolder(view);
+ viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType)));
+
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ int type = getItemViewType(position);
+ final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type));
+ final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type));
+
+ holder.mPermissionName.setText(title);
+ holder.mPermissionSummary.setText(summary);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mPermissions.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mPermissions != null ? mPermissions.size() : 0;
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mPermissionName;
+ private final TextView mPermissionSummary;
+ private final ImageView mPermissionIcon;
+ ViewHolder(View itemView) {
+ super(itemView);
+ mPermissionName = itemView.findViewById(R.id.permission_name);
+ mPermissionSummary = itemView.findViewById(R.id.permission_summary);
+ mPermissionIcon = itemView.findViewById(R.id.permission_icon);
+ }
+ }
+
+ void setPermissionType(List<Integer> permissions) {
+ mPermissions = permissions;
+ }
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index 76bbcfb79155..d5b2f0a748f1 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -121,6 +122,12 @@ class Utils {
return appInfo;
}
+ static @NonNull Drawable getIcon(@NonNull Context context, int resId) {
+ Drawable icon = context.getResources().getDrawable(resId, null);
+ icon.setTint(Color.DKGRAY);
+ return icon;
+ }
+
static void runOnMainThread(Runnable runnable) {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index a48f94b66def..da5f88dc3b7e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -189,14 +189,14 @@ public class NetworkIdentity {
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
- proto.write(NetworkIdentityProto.TYPE_FIELD_NUMBER, mType);
+ proto.write(NetworkIdentityProto.TYPE, mType);
// TODO: dump mRatType as well.
- proto.write(NetworkIdentityProto.ROAMING_FIELD_NUMBER, mRoaming);
- proto.write(NetworkIdentityProto.METERED_FIELD_NUMBER, mMetered);
- proto.write(NetworkIdentityProto.DEFAULT_NETWORK_FIELD_NUMBER, mDefaultNetwork);
- proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK_FIELD_NUMBER, mOemManaged);
+ proto.write(NetworkIdentityProto.ROAMING, mRoaming);
+ proto.write(NetworkIdentityProto.METERED, mMetered);
+ proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
+ proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
proto.end(start);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 56461babfe49..ad3a958a680e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -222,7 +222,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
final long start = proto.start(tag);
for (NetworkIdentity ident : this) {
- ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES_FIELD_NUMBER);
+ ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES);
}
proto.end(start);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 67d48f0000d5..735c44d5c87e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -732,19 +732,19 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
final long start = proto.start(tag);
for (Key key : getSortedKeys()) {
- final long startStats = proto.start(NetworkStatsCollectionProto.STATS_FIELD_NUMBER);
+ final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
// Key
- final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY_FIELD_NUMBER);
- key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY_FIELD_NUMBER);
- proto.write(NetworkStatsCollectionKeyProto.UID_FIELD_NUMBER, key.uid);
- proto.write(NetworkStatsCollectionKeyProto.SET_FIELD_NUMBER, key.set);
- proto.write(NetworkStatsCollectionKeyProto.TAG_FIELD_NUMBER, key.tag);
+ final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
+ key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY);
+ proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
+ proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
+ proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
proto.end(startKey);
// Value
final NetworkStatsHistory history = mStats.get(key);
- history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY_FIELD_NUMBER);
+ history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY);
proto.end(startStats);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 822a16e0bb41..301fef944169 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -915,18 +915,18 @@ public final class NetworkStatsHistory implements Parcelable {
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
- proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS_FIELD_NUMBER, bucketDuration);
+ proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
for (int i = 0; i < bucketCount; i++) {
- final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS_FIELD_NUMBER);
+ final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
- proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS_FIELD_NUMBER,
+ proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS,
bucketStart[i]);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES_FIELD_NUMBER, rxBytes, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS_FIELD_NUMBER, rxPackets, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES_FIELD_NUMBER, txBytes, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS_FIELD_NUMBER, txPackets, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS_FIELD_NUMBER, operations, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
proto.end(startBucket);
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index 151c90dd4155..3b93f1a1905f 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -25,9 +25,9 @@ import static android.net.NetworkStats.UID_ALL;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.NetworkStats;
import android.net.UnderlyingNetworkInfo;
+import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -35,6 +35,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ProcFileReader;
import com.android.net.module.util.CollectionUtils;
+import com.android.server.BpfNetMaps;
import libcore.io.IoUtils;
@@ -74,6 +75,8 @@ public class NetworkStatsFactory {
private final Context mContext;
+ private final BpfNetMaps mBpfNetMaps;
+
/**
* Guards persistent data access in this class
*
@@ -170,6 +173,7 @@ public class NetworkStatsFactory {
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
+ mBpfNetMaps = new BpfNetMaps();
synchronized (mPersistentDataLock) {
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
@@ -297,12 +301,14 @@ public class NetworkStatsFactory {
}
@GuardedBy("mPersistentDataLock")
- private void requestSwapActiveStatsMapLocked() {
- // Do a active map stats swap. When the binder call successfully returns,
- // the system server should be able to safely read and clean the inactive map
- // without race problem.
- final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
- cm.swapActiveStatsMap();
+ private void requestSwapActiveStatsMapLocked() throws IOException {
+ try {
+ // Do a active map stats swap. Once the swap completes, this code
+ // can read and clean the inactive map without races.
+ mBpfNetMaps.swapActiveStatsMap();
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e);
+ }
}
/**
@@ -328,11 +334,7 @@ public class NetworkStatsFactory {
final NetworkStats stats =
new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
if (mUseBpfStats) {
- try {
- requestSwapActiveStatsMapLocked();
- } catch (RuntimeException e) {
- throw new IOException(e);
- }
+ requestSwapActiveStatsMapLocked();
// Stats are always read from the inactive map, so they must be read after the
// swap
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index a006cd597568..f62765d07427 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -471,11 +471,11 @@ public class NetworkStatsRecorder {
public void dumpDebugLocked(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
if (mPending != null) {
- proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES_FIELD_NUMBER,
+ proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES,
mPending.getTotalBytes());
}
getOrLoadCompleteLocked().dumpDebug(proto,
- NetworkStatsRecorderProto.COMPLETE_HISTORY_FIELD_NUMBER);
+ NetworkStatsRecorderProto.COMPLETE_HISTORY);
proto.end(start);
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index ef6f39a5c040..b5e05395f2e8 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -2132,15 +2132,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO Right now it writes all history. Should it limit to the "since-boot" log?
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES_FIELD_NUMBER,
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES,
mActiveIfaces);
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES_FIELD_NUMBER,
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES,
mActiveUidIfaces);
- mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS_FIELD_NUMBER);
- mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS_FIELD_NUMBER);
- mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS_FIELD_NUMBER);
+ mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
+ mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
+ mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
mUidTagRecorder.dumpDebugLocked(proto,
- NetworkStatsServiceDumpProto.UID_TAG_STATS_FIELD_NUMBER);
+ NetworkStatsServiceDumpProto.UID_TAG_STATS);
proto.flush();
}
@@ -2150,8 +2150,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
for (int i = 0; i < ifaces.size(); i++) {
final long start = proto.start(tag);
- proto.write(NetworkInterfaceProto.INTERFACE_FIELD_NUMBER, ifaces.keyAt(i));
- ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES_FIELD_NUMBER);
+ proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
+ ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES);
proto.end(start);
}
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SettingsLib/res/drawable/ic_account_circle.xml
index 5ca99f32771b..5ca99f32771b 100644
--- a/packages/SystemUI/res/drawable/ic_account_circle.xml
+++ b/packages/SettingsLib/res/drawable/ic_account_circle.xml
diff --git a/packages/SystemUI/res/drawable/ic_account_circle_filled.xml b/packages/SettingsLib/res/drawable/ic_account_circle_filled.xml
index 47c553b52123..47c553b52123 100644
--- a/packages/SystemUI/res/drawable/ic_account_circle_filled.xml
+++ b/packages/SettingsLib/res/drawable/ic_account_circle_filled.xml
diff --git a/packages/SystemUI/res/drawable/ic_add_supervised_user.xml b/packages/SettingsLib/res/drawable/ic_add_supervised_user.xml
index 627743ed1669..627743ed1669 100644
--- a/packages/SystemUI/res/drawable/ic_add_supervised_user.xml
+++ b/packages/SettingsLib/res/drawable/ic_add_supervised_user.xml
diff --git a/packages/SystemUI/res/drawable/kg_bg_avatar.xml b/packages/SettingsLib/res/drawable/user_avatar_bg.xml
index addb3f7508f5..1f50496f3a5c 100644
--- a/packages/SystemUI/res/drawable/kg_bg_avatar.xml
+++ b/packages/SettingsLib/res/drawable/user_avatar_bg.xml
@@ -22,7 +22,7 @@
android:viewportHeight="100">
<path
- android:fillColor="@color/kg_user_switcher_avatar_background"
+ android:fillColor="@color/user_avatar_color_bg"
android:pathData="M50,50m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"/>
</vector>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 5e8779fa289a..7ab2ed9481b7 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -36,7 +36,8 @@
<color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
<color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
-
<color name="dark_mode_icon_color_single_tone">#99000000</color>
<color name="light_mode_icon_color_single_tone">#ffffff</color>
+
+ <color name="user_avatar_color_bg">?android:attr/colorBackgroundFloating</color>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index da0381b68278..e524405a39d5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1426,8 +1426,12 @@
<string name="user_switch_to_user">Switch to <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
<!-- Dialog message when creating a new user [CHAR LIMIT=NONE] -->
<string name="creating_new_user_dialog_message">Creating new user…</string>
+ <!-- Dialog message when creating a new guest [CHAR LIMIT=NONE] -->
+ <string name="creating_new_guest_dialog_message">Creating new guest…</string>
<!-- Text shown to notify that the creation of new user has failed. [CHAR LIMIT=40] -->
<string name="add_user_failed">Failed to create a new user</string>
+ <!-- Text shown to notify that the creation of new guest has failed. [CHAR LIMIT=40] -->
+ <string name="add_guest_failed">Failed to create a new guest</string>
<!-- Title for the preference to enter the nickname of the user to display in the user switcher [CHAR LIMIT=25]-->
<string name="user_nickname">Nickname</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 5e2f31010900..7573177565a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -24,9 +24,9 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothHidHost;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothPan;
@@ -159,10 +159,8 @@ public class LocalBluetoothProfileManager {
if (mHfpClientProfile == null && supportedList.contains(BluetoothProfile.HEADSET_CLIENT)) {
if (DEBUG) Log.d(TAG, "Adding local HfpClient profile");
mHfpClientProfile = new HfpClientProfile(mContext, mDeviceManager, this);
- addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
- BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
- BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
- BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
+ addProfile(mHfpClientProfile, HfpClientProfile.NAME,
+ BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
}
if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) {
if (DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile");
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index fcb56d2f7016..01d0cc40c0bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -248,9 +248,6 @@ public class DreamBackend {
}
public @WhenToDream int getWhenToDreamSetting() {
- if (!isEnabled()) {
- return NEVER;
- }
return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
: isActivatedOnDock() ? WHILE_DOCKED
: isActivatedOnSleep() ? WHILE_CHARGING
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index c7599623c465..3984ee9aa007 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -35,6 +35,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.NearbyDevice;
import android.text.TextUtils;
import android.util.Log;
@@ -77,6 +78,8 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
private int mConnectedRecord;
private int mState;
+ @NearbyDevice.RangeZone
+ private int mRangeZone = NearbyDevice.RANGE_UNKNOWN;
protected final Context mContext;
protected final MediaRoute2Info mRouteInfo;
@@ -136,6 +139,14 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
getId());
}
+ public @NearbyDevice.RangeZone int getRangeZone() {
+ return mRangeZone;
+ }
+
+ public void setRangeZone(@NearbyDevice.RangeZone int rangeZone) {
+ mRangeZone = rangeZone;
+ }
+
/**
* Get name from MediaDevice.
*
@@ -319,6 +330,11 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
}
}
+ // Both devices have same connection status, compare the range zone
+ if (NearbyDevice.compareRangeZones(getRangeZone(), another.getRangeZone()) != 0) {
+ return NearbyDevice.compareRangeZones(getRangeZone(), another.getRangeZone());
+ }
+
if (mType == another.mType) {
// Check device is muting expected device
if (isMutingExpectedDevice()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java b/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java
index 075635c87b1b..dd86bec9126c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserCreatingDialog.java
@@ -31,11 +31,15 @@ import com.android.settingslib.R;
public class UserCreatingDialog extends AlertDialog {
public UserCreatingDialog(Context context) {
+ this(context, false);
+ }
+
+ public UserCreatingDialog(Context context, boolean isGuest) {
// hardcoding theme to be consistent with UserSwitchingDialog's theme
// todo replace both to adapt to the device's theme
super(context, com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
- inflateContent();
+ inflateContent(isGuest);
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
@@ -44,12 +48,14 @@ public class UserCreatingDialog extends AlertDialog {
getWindow().setAttributes(attrs);
}
- private void inflateContent() {
+ private void inflateContent(boolean isGuest) {
// using the same design as UserSwitchingDialog
setCancelable(false);
View view = LayoutInflater.from(getContext())
.inflate(R.layout.user_creation_progress_dialog, null);
- String message = getContext().getString(R.string.creating_new_user_dialog_message);
+ String message = getContext().getString(isGuest
+ ? R.string.creating_new_guest_dialog_message
+ : R.string.creating_new_user_dialog_message);
view.setAccessibilityPaneTitle(message);
((TextView) view.findViewById(R.id.message)).setText(message);
setView(view);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index 9ebdba300266..1d08711715c3 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -150,23 +150,6 @@ public class AvatarPhotoControllerTest {
}
@Test
- public void takePhotoIsNotFollowedByCropIntentWhenCropNotSupported() throws IOException {
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
-
- File file = new File(mImagesDir, "file.txt");
- saveBitmapToFile(file);
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
- }
-
- @Test
public void choosePhotoIsFollowedByCrop() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index c122a37d0f79..179a498b7397 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -31,6 +31,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.NearbyDevice;
import android.os.Parcel;
import com.android.settingslib.bluetooth.A2dpProfile;
@@ -200,6 +201,18 @@ public class MediaDeviceTest {
}
@Test
+ public void compareTo_differentRange_sortWithRange() {
+ mBluetoothMediaDevice1.setRangeZone(NearbyDevice.RANGE_FAR);
+ mBluetoothMediaDevice2.setRangeZone(NearbyDevice.RANGE_CLOSE);
+ mMediaDevices.add(mBluetoothMediaDevice1);
+ mMediaDevices.add(mBluetoothMediaDevice2);
+
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
+ Collections.sort(mMediaDevices, COMPARATOR);
+ assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
+ }
+
+ @Test
public void compareTo_carKit_info_carKitFirst() {
when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
mMediaDevices.add(mInfoMediaDevice1);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1c5bf81faf3e..3c29a803ffbb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2252,12 +2252,14 @@ class SettingsProtoDumpUtil {
Settings.Secure.MULTI_PRESS_TIMEOUT,
SecureSettingsProto.MULTI_PRESS_TIMEOUT);
+ final long navBar = p.start(SecureSettingsProto.NAV_BAR);
dumpSetting(s, p,
Settings.Secure.NAV_BAR_FORCE_VISIBLE,
SecureSettingsProto.NavBar.NAV_BAR_FORCE_VISIBLE);
dumpSetting(s, p,
Settings.Secure.NAV_BAR_KIDS_MODE,
SecureSettingsProto.NavBar.NAV_BAR_KIDS_MODE);
+ p.end(navBar);
dumpSetting(s, p,
Settings.Secure.NAVIGATION_MODE,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c0e2b2ef361f..dae63a8b0e3c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -210,6 +210,7 @@
<uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <uses-permission android:name="android.permission.PROVISION_DEMO_DEVICE" />
<uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 183584227087..3f7e0f0fb527 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -33,6 +33,7 @@ import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
import com.android.internal.jank.InteractionJankMonitor
+import java.util.LinkedList
import kotlin.math.min
private const val TAG = "GhostedViewLaunchAnimatorController"
@@ -81,21 +82,51 @@ open class GhostedViewLaunchAnimatorController(
* [backgroundView].
*/
private var backgroundDrawable: WrappedDrawable? = null
- private val backgroundInsets by lazy { getBackground()?.opticalInsets ?: Insets.NONE }
+ private val backgroundInsets by lazy { background?.opticalInsets ?: Insets.NONE }
private var startBackgroundAlpha: Int = 0xFF
private val ghostedViewLocation = IntArray(2)
private val ghostedViewState = LaunchAnimator.State()
/**
- * Return the background of the [ghostedView]. This background will be used to draw the
- * background of the background view that is expanding up to the final animation position. This
- * is called at the start of the animation.
+ * The background of the [ghostedView]. This background will be used to draw the background of
+ * the background view that is expanding up to the final animation position.
*
* Note that during the animation, the alpha value value of this background will be set to 0,
* then set back to its initial value at the end of the animation.
*/
- protected open fun getBackground(): Drawable? = ghostedView.background
+ private val background: Drawable?
+
+ init {
+ /** Find the first view with a background in [view] and its children. */
+ fun findBackground(view: View): Drawable? {
+ if (view.background != null) {
+ return view.background
+ }
+
+ // Perform a BFS to find the largest View with background.
+ val views = LinkedList<View>().apply {
+ add(view)
+ }
+
+ while (views.isNotEmpty()) {
+ val v = views.removeFirst()
+ if (v.background != null) {
+ return v.background
+ }
+
+ if (v is ViewGroup) {
+ for (i in 0 until v.childCount) {
+ views.add(v.getChildAt(i))
+ }
+ }
+ }
+
+ return null
+ }
+
+ background = findBackground(ghostedView)
+ }
/**
* Set the corner radius of [background]. The background is the one that was returned by
@@ -113,7 +144,7 @@ open class GhostedViewLaunchAnimatorController(
/** Return the current top corner radius of the background. */
protected open fun getCurrentTopCornerRadius(): Float {
- val drawable = getBackground() ?: return 0f
+ val drawable = background ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
@@ -122,7 +153,7 @@ open class GhostedViewLaunchAnimatorController(
/** Return the current bottom corner radius of the background. */
protected open fun getCurrentBottomCornerRadius(): Float {
- val drawable = getBackground() ?: return 0f
+ val drawable = background ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
@@ -162,9 +193,8 @@ open class GhostedViewLaunchAnimatorController(
// We wrap the ghosted view background and use it to draw the expandable background. Its
// alpha will be set to 0 as soon as we start drawing the expanding background.
- val drawable = getBackground()
- startBackgroundAlpha = drawable?.alpha ?: 0xFF
- backgroundDrawable = WrappedDrawable(drawable)
+ startBackgroundAlpha = background?.alpha ?: 0xFF
+ backgroundDrawable = WrappedDrawable(background)
backgroundView?.background = backgroundDrawable
// Create a ghost of the view that will be moving and fading out. This allows to fade out
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index c97ebe8d5559..e74b6c78ec80 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -41,6 +41,14 @@
-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.Dagger** { !synthetic *; }
-keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.Dagger** { !synthetic *; }
+# Prevent optimization or access modification of any referenced code that may
+# conflict with code in the bootclasspath.
+# TODO(b/222468116): Resolve such collisions in the build system.
+-keepnames class android.**.nano.** { *; }
+-keepnames class com.android.**.nano.** { *; }
+-keepnames class com.android.internal.protolog.** { *; }
+-keepnames class android.hardware.common.** { *; }
+
# Allows proguard to make private and protected methods and fields public as
# part of optimization. This lets proguard inline trivial getter/setter methods.
-allowaccessmodification
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
index b96c07ea53f0..07642736869c 100644
--- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -15,10 +15,11 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?attr/wallpaperTextColorSecondary">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent"/>
+ <solid android:color="?androidprv:attr/colorAccentTertiary"/>
<corners android:radius="24dp"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/color/kg_user_avatar_frame.xml b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
index 174981e2a660..a143194f331d 100644
--- a/packages/SystemUI/res/color/kg_user_avatar_frame.xml
+++ b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
@@ -18,6 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
- android:color="@color/kg_user_switcher_avatar_background" />
- <item android:color="@color/kg_user_switcher_avatar_background" />
+ android:color="@color/user_avatar_color_bg" />
+ <item android:color="@color/user_avatar_color_bg" />
</selector>
diff --git a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml
index b78b2216c9f9..1ed75537059a 100644
--- a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml
+++ b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml
@@ -36,7 +36,7 @@
</item>
<!-- Where the user drawable/bitmap will be placed -->
<item
- android:drawable="@drawable/kg_bg_avatar"
+ android:drawable="@drawable/user_avatar_bg"
android:width="@dimen/bouncer_user_switcher_icon_size"
android:height="@dimen/bouncer_user_switcher_icon_size"
android:top="@dimen/user_switcher_icon_large_margin"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index c58c00114262..27823009459c 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -26,7 +26,7 @@
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:src="@drawable/overlay_actions_background_protection"/>
- <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
+ <com.android.systemui.screenshot.DraggableConstraintLayout
android:id="@+id/clipboard_ui"
android:theme="@style/FloatingOverlay"
android:layout_width="match_parent"
@@ -146,5 +146,5 @@
android:layout_margin="@dimen/overlay_dismiss_button_margin"
android:src="@drawable/overlay_cancel"/>
</FrameLayout>
- </com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
+ </com.android.systemui.screenshot.DraggableConstraintLayout>
</FrameLayout> \ 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 978998d6ca2c..9ad01528686e 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -139,7 +139,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
- android:layout_marginTop="1dp"
+ android:layout_marginTop="0dp"
app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toStartOf="@id/actionPlayPause"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 813bb6018801..8de80844d784 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -14,25 +14,25 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<com.android.systemui.screenshot.DraggableConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/screenshot_actions_container_background"
+ android:id="@+id/actions_container_background"
android:visibility="gone"
android:layout_height="0dp"
android:layout_width="0dp"
android:elevation="1dp"
android:background="@drawable/action_chip_container_background"
android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
- app:layout_constraintBottom_toBottomOf="@+id/screenshot_actions_container"
+ app:layout_constraintBottom_toBottomOf="@+id/actions_container"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/screenshot_actions_container"
- app:layout_constraintEnd_toEndOf="@+id/screenshot_actions_container"/>
+ app:layout_constraintTop_toTopOf="@+id/actions_container"
+ app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
<HorizontalScrollView
- android:id="@+id/screenshot_actions_container"
+ android:id="@+id/actions_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
@@ -130,4 +130,4 @@
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
android:elevation="@dimen/overlay_preview_elevation"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e4706e263a0f..ef030baa967b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -26,9 +26,6 @@
android:layout_height="match_parent"
android:background="@android:color/transparent">
- <include layout="@layout/communal_host_view"
- android:visibility="gone"/>
-
<ViewStub
android:id="@+id/keyguard_qs_user_switch_stub"
android:layout="@layout/keyguard_qs_user_switch"
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 4f95811b21b3..c56ba7b6b1ce 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -22,6 +22,8 @@
<dimen name="ambient_indication_margin_bottom">115dp</dimen>
<dimen name="lock_icon_margin_bottom">60dp</dimen>
+ <dimen name="qs_media_session_height_expanded">172dp</dimen>
+
<!-- margin from keyguard status bar to clock. For split shade it should be
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
@@ -34,17 +36,47 @@
<item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item>
<dimen name="controls_task_view_right_margin">8dp</dimen>
- <!-- Distance that the full shade transition takes in order for qs to fully transition to the
- shade -->
- <dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
+ <dimen name="split_shade_header_height">42dp</dimen>
- <!-- Distance that the full shade transition takes in order for scrim to fully transition to
- the shade (in alpha) -->
- <dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen>
+ <!-- Distance that the full shade transition takes in order to complete by tapping on a button
+ like "expand". -->
+ <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order to complete. -->
+ <dimen name="lockscreen_shade_full_transition_distance">200dp</dimen>
<!-- Distance that the full shade transition takes in order for media to fully transition to
- the shade -->
+ the shade -->
<dimen name="lockscreen_shade_media_transition_distance">200dp</dimen>
- <dimen name="notification_panel_margin_horizontal">12dp</dimen>
+ <!-- Distance that the full shade transition takes in order for scrim to fully transition to
+ the shade (in alpha) -->
+ <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the keyguard content on
+ NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) -->
+ <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">80dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the notification shell to fully
+ expand. -->
+ <dimen name="lockscreen_shade_notif_shelf_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the Quick Settings to fully
+ fade and expand. -->
+ <dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for depth of the wallpaper to fully
+ change.
+ On split-shade, there should be no depth effect, so setting the value to 0. -->
+ <dimen name="lockscreen_shade_depth_controller_transition_distance">0dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the UDFPS Keyguard View to fully
+ fade. -->
+ <dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Used for StatusBar to know that a transition is in progress. At the moment it only checks
+ whether the progress is > 0, therefore this value is not very important. -->
+ <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <dimen name="notification_panel_margin_horizontal">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 71c195896051..f267088ea203 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -23,5 +23,7 @@
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
- <dimen name="notification_panel_margin_horizontal">24dp</dimen>
+ <dimen name="split_shade_header_height">56dp</dimen>
+
+ <dimen name="qs_media_session_height_expanded">184dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index dc7470081da2..1edaaadfb433 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,7 +52,7 @@
(e.g. cannot be switched to) -->
<color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color>
<!-- Color of background circle of user avatars in keyguard user switcher -->
- <color name="kg_user_switcher_avatar_background">?android:attr/colorBackgroundFloating</color>
+ <color name="user_avatar_color_bg">?android:attr/colorBackgroundFloating</color>
<!-- Icon color for user avatars in user switcher quick settings -->
<color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 398ea26cf815..c64beb58449c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1126,13 +1126,40 @@
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
<dimen name="media_output_dialog_app_tier_icon_size">20dp</dimen>
- <!-- Distance that the full shade transition takes in order for qs to fully transition to the
- shade -->
- <dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
+ <!-- Distance that the full shade transition takes in order to complete by tapping on a button
+ like "expand". -->
+ <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order to complete. -->
+ <dimen name="lockscreen_shade_full_transition_distance">80dp</dimen>
<!-- Distance that the full shade transition takes in order for scrim to fully transition to
the shade (in alpha) -->
- <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
+ <dimen name="lockscreen_shade_scrim_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the keyguard content on
+ NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) -->
+ <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the notification shelf to fully
+ expand. -->
+ <dimen name="lockscreen_shade_notif_shelf_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the Quick Settings to fully
+ fade and expand. -->
+ <dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for depth of the wallpaper to fully
+ change. -->
+ <dimen name="lockscreen_shade_depth_controller_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Distance that the full shade transition takes in order for the UDFPS Keyguard View to fully
+ fade. -->
+ <dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
+
+ <!-- Used for StatusBar to know that a transition is in progress. At the moment it only checks
+ whether the progress is > 0, therefore this value is not very important. -->
+ <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
<!-- Distance that the full shade transition takes in order for media to fully transition to
the shade -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java
deleted file mode 100644
index 325bcfc622d7..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.recents.utilities;
-
-import android.graphics.Bitmap;
-import android.graphics.ColorSpace;
-import android.graphics.ParcelableColorSpace;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-
-import java.util.Objects;
-
-/**
- * Utils for working with Bitmaps.
- */
-public final class BitmapUtil {
- private static final String KEY_BUFFER = "bitmap_util_buffer";
- private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";
-
- private BitmapUtil(){ }
-
- /**
- * Creates a Bundle that represents the given Bitmap.
- * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
- * copies when passing across processes, only pass to processes you trust.
- *
- * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
- * returned Bundle should be treated as a standalone object.
- *
- * @param bitmap to convert to bundle
- * @return a Bundle representing the bitmap, should only be parsed by
- * {@link #bundleToHardwareBitmap(Bundle)}
- */
- public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
- if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
- throw new IllegalArgumentException(
- "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
- }
-
- // Bitmap assumes SRGB for null color space
- ParcelableColorSpace colorSpace =
- bitmap.getColorSpace() == null
- ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
- : new ParcelableColorSpace(bitmap.getColorSpace());
-
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
- bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);
-
- return bundle;
- }
-
- /**
- * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
- *
- * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing this
- * Bitmap on to any other source.
- *
- * @param bundle containing the bitmap
- * @return a hardware Bitmap
- */
- public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
- if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
- throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
- }
-
- HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER);
- ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE);
-
- return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
- colorSpace.getColorSpace());
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index c4b02f62f291..ffd15460ce91 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -107,10 +107,10 @@ public class EmergencyButton extends Button {
return super.performLongClick();
}
- void updateEmergencyCallButton(boolean isInCall, boolean isVoiceCapable, boolean simLocked) {
+ void updateEmergencyCallButton(boolean isInCall, boolean hasTelephonyRadio, boolean simLocked) {
boolean visible = false;
- if (isVoiceCapable) {
- // Emergency calling requires voice capability.
+ if (hasTelephonyRadio) {
+ // Emergency calling requires a telephony radio.
if (isInCall) {
visible = true; // always show "return to call" if phone is off-hook
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index e7215b8ebe49..f28910576073 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -21,6 +21,7 @@ import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -116,7 +117,8 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
if (mView != null) {
mView.updateEmergencyCallButton(
mTelecomManager != null && mTelecomManager.isInCall(),
- mTelephonyManager.isVoiceCapable(),
+ getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY),
mKeyguardUpdateMonitor.isSimPinVoiceSecure());
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 7f456dba6053..eb418ff3b142 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -194,9 +194,12 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
}
mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
+ startErrorAnimation();
}
}
+ protected void startErrorAnimation() { /* no-op */ }
+
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0529cdbcbb13..28f21af5b56b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -183,7 +183,7 @@ public class KeyguardPasswordViewController
@Override
void resetState() {
mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- mMessageAreaController.setMessage("");
+ mMessageAreaController.setMessage(R.string.keyguard_enter_your_password);
final boolean wasDisabled = mPasswordEntry.isEnabled();
mView.setPasswordEntryEnabled(true);
mView.setPasswordEntryInputEnabled(true);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 41f92407a683..7635f919567a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -358,7 +358,7 @@ public class KeyguardPatternViewController
}
private void displayDefaultSecurityMessage() {
- mMessageAreaController.setMessage("");
+ mMessageAreaController.setMessage(R.string.keyguard_enter_your_pattern);
}
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4723af2760c0..c46e33d9fd53 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -24,6 +24,9 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -32,6 +35,10 @@ import android.view.View;
import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* A Pin based Keyguard input view
@@ -188,4 +195,48 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
}
+
+ /**
+ * Begins an error animation for this view.
+ **/
+ public void startErrorAnimation() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ List<Animator> animators = new ArrayList();
+ List<View> buttons = new ArrayList<>();
+ for (int i = 1; i <= 9; i++) {
+ buttons.add(mButtons[i]);
+ }
+ buttons.add(mDeleteButton);
+ buttons.add(mButtons[0]);
+ buttons.add(mOkButton);
+
+ int delay = 0;
+ for (int i = 0; i < buttons.size(); i++) {
+ final View button = buttons.get(i);
+ AnimatorSet animateWrapper = new AnimatorSet();
+ animateWrapper.setStartDelay(delay);
+
+ ValueAnimator scaleDownAnimator = ValueAnimator.ofFloat(1f, 0.8f);
+ scaleDownAnimator.setInterpolator(Interpolators.STANDARD);
+ scaleDownAnimator.addUpdateListener(valueAnimator -> {
+ button.setScaleX((float) valueAnimator.getAnimatedValue());
+ button.setScaleY((float) valueAnimator.getAnimatedValue());
+ });
+ scaleDownAnimator.setDuration(50);
+
+ ValueAnimator scaleUpAnimator = ValueAnimator.ofFloat(0.8f, 1f);
+ scaleUpAnimator.setInterpolator(Interpolators.STANDARD);
+ scaleUpAnimator.addUpdateListener(valueAnimator -> {
+ button.setScaleX((float) valueAnimator.getAnimatedValue());
+ button.setScaleY((float) valueAnimator.getAnimatedValue());
+ });
+ scaleUpAnimator.setDuration(617);
+
+ animateWrapper.playSequentially(scaleDownAnimator, scaleUpAnimator);
+ animators.add(animateWrapper);
+ delay += 33;
+ }
+ animatorSet.playTogether(animators);
+ animatorSet.start();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 8de4e7b557f3..cc7e4f7a0d9a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -132,5 +132,12 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
@Override
void resetState() {
mView.setPasswordEntryEnabled(true);
+ mMessageAreaController.setMessage(R.string.keyguard_enter_your_pin);
+ }
+
+ @Override
+ protected void startErrorAnimation() {
+ super.startErrorAnimation();
+ mView.startErrorAnimation();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9f4585fb1a92..160d82af98fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -76,12 +76,6 @@ public class KeyguardPinViewController
}
@Override
- void resetState() {
- super.resetState();
- mMessageAreaController.setMessage("");
- }
-
- @Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index af7cf862b6b6..362fbed7055a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -918,7 +918,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
drawable.setTint(iconColor);
- Drawable bg = context.getDrawable(R.drawable.kg_bg_avatar);
+ Drawable bg = context.getDrawable(R.drawable.user_avatar_bg);
bg.setTintBlendMode(BlendMode.DST);
bg.setTint(Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.colorSurfaceVariant));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index a5fe0efc8887..af3da9f940bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,7 +20,6 @@ import android.graphics.Rect;
import android.util.Slog;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
-import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -63,7 +62,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardClockSwitchController keyguardClockSwitchController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- CommunalStateController communalStateController,
ConfigurationController configurationController,
DozeParameters dozeParameters,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -75,9 +73,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mConfigurationController = configurationController;
mDozeParameters = dozeParameters;
mKeyguardStateController = keyguardStateController;
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
- keyguardStateController, dozeParameters, screenOffAnimationController,
- /* animateYPos= */ true, /* visibleOnCommunal= */ false);
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
+ dozeParameters, screenOffAnimationController, /* animateYPos= */ true);
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 37f45644fa68..1ef6dea4e680 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -52,6 +52,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -752,15 +753,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- private void handleFingerprintAcquired(int acquireInfo) {
+ private void handleFingerprintAcquired(
+ @BiometricFingerprintConstants.FingerprintAcquired int acquireInfo) {
Assert.isMainThread();
- if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
- return;
- }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+ cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT, acquireInfo);
}
}
}
@@ -960,14 +959,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void handleFaceAcquired(int acquireInfo) {
Assert.isMainThread();
- if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
- return;
- }
- if (DEBUG_FACE) Log.d(TAG, "Face acquired");
+ if (DEBUG_FACE) Log.d(TAG, "Face acquired acquireInfo=" + acquireInfo);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FACE);
+ cb.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8d5603dc1563..ad2053cbc31b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -206,8 +206,10 @@ public class KeyguardUpdateMonitorCallback {
* It is guaranteed that either {@link #onBiometricAuthenticated} or
* {@link #onBiometricAuthFailed(BiometricSourceType)} is called after this method eventually.
* @param biometricSourceType
+ * @param acquireInfo see {@link android.hardware.biometrics.BiometricFaceConstants} and
+ * {@link android.hardware.biometrics.BiometricFingerprintConstants}
*/
- public void onBiometricAcquired(BiometricSourceType biometricSourceType) { }
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) { }
/**
* Called when a biometric couldn't be authenticated.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index bb608c79a50e..498304b3fc55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -22,7 +22,6 @@ import android.view.View;
import android.view.ViewPropertyAnimator;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -39,30 +38,24 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
public class KeyguardVisibilityHelper {
private View mView;
- private final CommunalStateController mCommunalStateController;
private final KeyguardStateController mKeyguardStateController;
private final DozeParameters mDozeParameters;
private final ScreenOffAnimationController mScreenOffAnimationController;
- private final boolean mVisibleOnCommunal;
private boolean mAnimateYPos;
private boolean mKeyguardViewVisibilityAnimating;
private boolean mLastOccludedState = false;
private final AnimationProperties mAnimationProperties = new AnimationProperties();
public KeyguardVisibilityHelper(View view,
- CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
DozeParameters dozeParameters,
ScreenOffAnimationController screenOffAnimationController,
- boolean animateYPos,
- boolean visibleOnCommunal) {
+ boolean animateYPos) {
mView = view;
- mCommunalStateController = communalStateController;
mKeyguardStateController = keyguardStateController;
mDozeParameters = dozeParameters;
mScreenOffAnimationController = screenOffAnimationController;
mAnimateYPos = animateYPos;
- mVisibleOnCommunal = visibleOnCommunal;
}
public boolean isVisibilityAnimating() {
@@ -81,13 +74,6 @@ public class KeyguardVisibilityHelper {
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
- // If the communal view is showing, hide immediately
- if (!mVisibleOnCommunal && mCommunalStateController.getCommunalViewShowing()) {
- mView.setVisibility(View.GONE);
- mView.setAlpha(1f);
- return;
- }
-
if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
&& statusBarState != KEYGUARD) || goingToFullShade) {
mKeyguardViewVisibilityAnimating = true;
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 00470c0efe35..f925eaa0e40b 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -73,12 +73,14 @@ class NumPadAnimator {
ValueAnimator expandBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
mNormalColor, mHighlightColor);
expandBackgroundColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS);
+ expandBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
expandBackgroundColorAnimator.addUpdateListener(
animator -> mBackground.setColor((int) animator.getAnimatedValue()));
ValueAnimator expandTextColorAnimator =
ValueAnimator.ofObject(new ArgbEvaluator(),
textColorPrimary, textColorPrimaryInverse);
+ expandTextColorAnimator.setInterpolator(Interpolators.LINEAR);
expandTextColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS);
expandTextColorAnimator.addUpdateListener(valueAnimator -> {
if (digitTextView != null) {
@@ -98,6 +100,7 @@ class NumPadAnimator {
anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue()));
ValueAnimator contractBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
mHighlightColor, mNormalColor);
+ contractBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
contractBackgroundColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS);
contractBackgroundColorAnimator.setDuration(CONTRACT_ANIMATION_MS);
contractBackgroundColorAnimator.addUpdateListener(
@@ -106,6 +109,7 @@ class NumPadAnimator {
ValueAnimator contractTextColorAnimator =
ValueAnimator.ofObject(new ArgbEvaluator(), textColorPrimaryInverse,
textColorPrimary);
+ contractTextColorAnimator.setInterpolator(Interpolators.LINEAR);
contractTextColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS);
contractTextColorAnimator.setDuration(CONTRACT_ANIMATION_MS);
contractTextColorAnimator.addUpdateListener(valueAnimator -> {
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index bc2a1ff24235..7afd43d1cb06 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
@@ -78,7 +79,8 @@ public class LatencyTester extends CoreStartable {
}
private void fakeWakeAndUnlock(BiometricSourceType type) {
- mBiometricUnlockController.onBiometricAcquired(type);
+ mBiometricUnlockController.onBiometricAcquired(type,
+ BiometricConstants.BIOMETRIC_ACQUIRED_GOOD);
mBiometricUnlockController.onBiometricAuthenticated(
KeyguardUpdateMonitor.getCurrentUser(), type, true /* isStrongBiometric */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 6a60afa8eadf..ff5715c606b6 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -71,7 +71,9 @@ public final class Prefs {
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP,
- Key.ACCESSIBILITY_FLOATING_MENU_POSITION
+ Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
+ Key.HAS_CLICKED_NUDGE_TO_SETUP_DREAM,
+ Key.HAS_DISMISSED_NUDGE_TO_SETUP_DREAM
})
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
public @interface Key {
@@ -115,6 +117,8 @@ public final class Prefs {
String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
"HasSeenAccessibilityFloatingMenuDockTooltip";
String ACCESSIBILITY_FLOATING_MENU_POSITION = "AccessibilityFloatingMenuPosition";
+ String HAS_CLICKED_NUDGE_TO_SETUP_DREAM = "HasClickedNudgeToSetupDream";
+ String HAS_DISMISSED_NUDGE_TO_SETUP_DREAM = "HasDismissedNudgeToSetupDream";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 50ca447090b5..7e1a02626dc9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1072,6 +1072,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
pw.println(" mScale:" + mScale);
+ pw.println(" mWindowBounds:" + mWindowBounds);
pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
pw.println(" mMagnificationFrameBoundary:"
+ (isWindowVisible() ? mMagnificationFrameBoundary : "empty"));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 99f27d7f48e7..a27b9cd357f4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PointF
+import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricSourceType
import android.util.DisplayMetrics
import android.util.Log
@@ -39,8 +40,8 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -257,6 +258,16 @@ class AuthRippleController @Inject constructor(
override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
mView.retractRipple()
}
+
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType?,
+ acquireInfo: Int
+ ) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
+ acquireInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL) {
+ mView.retractRipple()
+ }
+ }
}
private val configurationChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 8052c2071d86..bf42db53d3d8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,6 +16,7 @@
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;
@@ -30,6 +31,7 @@ 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;
@@ -141,11 +143,11 @@ public class UdfpsController implements DozeReceiver {
private int mActivePointerId = -1;
// The timestamp of the most recent touch log.
private long mTouchLogTime;
- // Sensor has a good capture for this touch. Do not need to illuminate for this particular
- // touch event anymore. In other words, do not illuminate until user lifts and touches the
- // sensor area again.
+ // Sensor has a capture (good or bad) for this touch. Do not need to illuminate for this
+ // particular touch event anymore. In other words, do not illuminate until user lifts and
+ // touches the sensor area again.
// TODO: We should probably try to make touch/illumination things more of a FSM
- private boolean mGoodCaptureReceived;
+ private boolean mAcquiredReceived;
// The current request from FingerprintService. Null if no current request.
@Nullable UdfpsControllerOverlay mOverlay;
@@ -221,19 +223,28 @@ public class UdfpsController implements DozeReceiver {
}
@Override
- public void onAcquiredGood(int sensorId) {
- mFgExecutor.execute(() -> {
- if (mOverlay == null) {
- Log.e(TAG, "Null request when onAcquiredGood for sensorId: " + sensorId);
- return;
- }
- mGoodCaptureReceived = true;
- final UdfpsView view = mOverlay.getOverlayView();
- if (view != null) {
- view.stopIllumination();
- }
- mOverlay.onAcquiredGood();
- });
+ public void onAcquired(
+ int sensorId,
+ @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
+ ) {
+ if (BiometricFingerprintConstants.shouldTurnOffHbm(acquiredInfo)) {
+ boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
+ mFgExecutor.execute(() -> {
+ if (mOverlay == null) {
+ Log.e(TAG, "Null request when onAcquired for sensorId: " + sensorId
+ + " acquiredInfo=" + acquiredInfo);
+ return;
+ }
+ mAcquiredReceived = true;
+ final UdfpsView view = mOverlay.getOverlayView();
+ if (view != null) {
+ view.stopIllumination(); // turn off HBM
+ }
+ if (acquiredGood) {
+ mOverlay.onAcquiredGood();
+ }
+ });
+ }
}
@Override
@@ -414,8 +425,8 @@ public class UdfpsController implements DozeReceiver {
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
- if (!isIlluminationRequested && !mGoodCaptureReceived &&
- !exceedsVelocityThreshold) {
+ if (!isIlluminationRequested && !mAcquiredReceived
+ && !exceedsVelocityThreshold) {
final int rawX = (int) event.getRawX();
final int rawY = (int) event.getRawY();
// Default coordinates assume portrait mode.
@@ -799,7 +810,7 @@ public class UdfpsController implements DozeReceiver {
private void onFingerUp(@NonNull UdfpsView view) {
mExecution.assertIsMainThread();
mActivePointerId = -1;
- mGoodCaptureReceived = false;
+ mAcquiredReceived = false;
if (mOnFingerDown) {
mFingerprintManager.onPointerUp(mSensorProps.sensorId);
for (Callback cb : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 56e4d7ec1882..0f937d163f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -73,6 +73,7 @@ import android.widget.TextView;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.R;
+import com.android.systemui.screenshot.DraggableConstraintLayout;
import com.android.systemui.screenshot.FloatingWindowUtil;
import com.android.systemui.screenshot.OverlayActionChip;
import com.android.systemui.screenshot.TimeoutHandler;
@@ -166,8 +167,28 @@ public class ClipboardOverlayController {
mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
- mView.setOnDismissEndCallback(this::hideImmediate);
- mView.setOnInteractionCallback(mTimeoutHandler::resetTimeout);
+ mView.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
+ @Override
+ public void onInteraction() {
+ mTimeoutHandler.resetTimeout();
+ }
+
+ @Override
+ public void onSwipeDismissInitiated(Animator animator) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mContainer.animate().alpha(0).start();
+ }
+ });
+ }
+
+ @Override
+ public void onDismissComplete() {
+ hideImmediate();
+ }
+ });
mDismissButton.setOnClickListener(view -> animateOut());
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
deleted file mode 100644
index a327809640db..000000000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.clipboardoverlay;
-
-import android.animation.Animator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.screenshot.SwipeDismissHandler;
-
-import java.util.function.Consumer;
-
-/**
- * ConstraintLayout that is draggable when touched in a specific region
- */
-public class DraggableConstraintLayout extends ConstraintLayout {
- private final SwipeDismissHandler mSwipeDismissHandler;
- private final GestureDetector mSwipeDetector;
- private Consumer<Animator> mOnDismissInitiated;
- private Runnable mOnDismissComplete;
- private Runnable mOnInteraction;
-
- public DraggableConstraintLayout(Context context) {
- this(context, null);
- }
-
- public DraggableConstraintLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DraggableConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- mSwipeDismissHandler = new SwipeDismissHandler(mContext, this,
- new SwipeDismissHandler.SwipeDismissCallbacks() {
- @Override
- public void onInteraction() {
- if (mOnInteraction != null) {
- mOnInteraction.run();
- }
- }
-
- @Override
- public void onSwipeDismissInitiated(Animator animator) {
- if (mOnDismissInitiated != null) {
- mOnDismissInitiated.accept(animator);
- }
- }
-
- @Override
- public void onDismissComplete() {
- if (mOnDismissComplete != null) {
- mOnDismissComplete.run();
- }
- }
- });
- setOnTouchListener(mSwipeDismissHandler);
-
- mSwipeDetector = new GestureDetector(mContext,
- new GestureDetector.SimpleOnGestureListener() {
- final Rect mActionsRect = new Rect();
-
- @Override
- public boolean onScroll(
- MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
- View actionsContainer = findViewById(R.id.actions_container);
- actionsContainer.getBoundsOnScreen(mActionsRect);
- // return true if we aren't in the actions bar, or if we are but it isn't
- // scrollable in the direction of movement
- return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
- || !actionsContainer.canScrollHorizontally((int) distanceX);
- }
- });
- mSwipeDetector.setIsLongpressEnabled(false);
- }
-
- @Override // View
- protected void onFinishInflate() {
-
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mSwipeDismissHandler.onTouch(this, ev);
- }
-
- return mSwipeDetector.onTouchEvent(ev);
- }
-
- /**
- * Dismiss the view, with animation controlled by SwipeDismissHandler
- */
- public void dismiss() {
- mSwipeDismissHandler.dismiss();
- }
-
- /**
- * Set the callback to be run after view is dismissed (before animation; receives animator as
- * input)
- */
- public void setOnDismissStartCallback(Consumer<Animator> callback) {
- mOnDismissInitiated = callback;
- }
-
- /**
- * Set the callback to be run after view is dismissed
- */
- public void setOnDismissEndCallback(Runnable callback) {
- mOnDismissComplete = callback;
- }
-
- /**
- * Set the callback to be run when the view is interacted with (e.g. tapped)
- */
- public void setOnInteractionCallback(Runnable callback) {
- mOnInteraction = callback;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
deleted file mode 100644
index 6e2fcd11f643..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Container for communal presentation. Containing communal-related view to this parent view allows
- * for aggregate measurement/layout adjustments and capturing said values before the communal views
- * might be available.
- */
-public class CommunalHostView extends FrameLayout {
- public CommunalHostView(@NonNull Context context) {
- this(context, null, 0);
- }
-
- public CommunalHostView(@NonNull Context context,
- @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CommunalHostView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
deleted file mode 100644
index b42838099986..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.KeyguardVisibilityHelper;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ViewController;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Injectable controller for {@link CommunalHostView}.
- */
-public class CommunalHostViewController extends ViewController<CommunalHostView> {
- private static final String TAG = "CommunalController";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String STATE_LIST_FORMAT = "[%s]";
- private static final AnimationProperties COMMUNAL_ANIMATION_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-
- private final Executor mMainExecutor;
- private final CommunalStateController mCommunalStateController;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final KeyguardStateController mKeyguardStateController;
- private final StatusBarStateController mStatusBarStateController;
- private WeakReference<CommunalSource> mCurrentSource;
- private Optional<ShowRequest> mLastRequest = Optional.empty();
- private int mState;
- private float mQsExpansion;
- private float mShadeExpansion;
-
- @Retention(RetentionPolicy.RUNTIME)
- @IntDef({STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_BOUNCER_SHOWING, STATE_KEYGUARD_OCCLUDED})
- public @interface State {}
-
- private static final int STATE_KEYGUARD_SHOWING = 1 << 0;
- private static final int STATE_DOZING = 1 << 1;
- private static final int STATE_BOUNCER_SHOWING = 1 << 2;
- private static final int STATE_KEYGUARD_OCCLUDED = 1 << 3;
-
- // Only show communal view when keyguard is showing and not dozing.
- private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
- private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
- STATE_DOZING | STATE_KEYGUARD_OCCLUDED;
-
- private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
-
- private ViewController<? extends View> mCommunalViewController;
-
- private static class ShowRequest {
- private boolean mShouldShow;
- private WeakReference<CommunalSource> mSource;
-
- ShowRequest(boolean shouldShow, WeakReference<CommunalSource> source) {
- mShouldShow = shouldShow;
- mSource = source;
- }
-
- CommunalSource getSource() {
- return mSource != null ? mSource.get() : null;
- }
-
- boolean shouldShow() {
- return mShouldShow;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof ShowRequest)) return false;
- ShowRequest that = (ShowRequest) o;
- return mShouldShow == that.mShouldShow && Objects.equals(getSource(), that.getSource());
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mShouldShow, mSource);
- }
- }
-
- private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- if (DEBUG) {
- Log.d(TAG, "onKeyguardBouncerChanged:" + bouncer);
- }
-
- setState(STATE_BOUNCER_SHOWING, bouncer);
- }
-
- @Override
- public void onKeyguardOccludedChanged(boolean occluded) {
- if (DEBUG) {
- Log.d(TAG, "onKeyguardOccludedChanged" + occluded);
- }
-
- setState(STATE_KEYGUARD_OCCLUDED, occluded);
- }
- };
-
- private KeyguardStateController.Callback mKeyguardCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- final boolean isShowing = mKeyguardStateController.isShowing();
- if (DEBUG) {
- Log.d(TAG, "setKeyguardShowing:" + isShowing);
- }
-
- setState(STATE_KEYGUARD_SHOWING, isShowing);
- }
- };
-
- private StatusBarStateController.StateListener mDozeCallback =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (DEBUG) {
- Log.d(TAG, "setDozing:" + isDozing);
- }
-
- setState(STATE_DOZING, isDozing);
- }
-
- @Override
- public void onStateChanged(int newState) {
- updateCommunalViewOccluded();
- }
- };
-
- @Inject
- protected CommunalHostViewController(@Main Executor mainExecutor,
- CommunalStateController communalStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardStateController keyguardStateController,
- DozeParameters dozeParameters,
- ScreenOffAnimationController screenOffAnimationController,
- StatusBarStateController statusBarStateController, CommunalHostView view) {
- super(view);
- mCommunalStateController = communalStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mMainExecutor = mainExecutor;
- mKeyguardStateController = keyguardStateController;
- mStatusBarStateController = statusBarStateController;
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
- keyguardStateController, dozeParameters, screenOffAnimationController,
- /* animateYPos= */ false, /* visibleOnCommunal= */ true);
- }
-
- /**
- * Set the visibility of the keyguard status view based on some new state.
- */
- public void setKeyguardStatusViewVisibility(
- int statusBarState,
- boolean keyguardFadingAway,
- boolean goingToFullShade,
- int oldStatusBarState) {
- mKeyguardVisibilityHelper.setViewVisibility(
- statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
- }
-
- /**
- * Set keyguard status view alpha.
- */
- public void setAlpha(float alpha) {
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
- mView.setAlpha(alpha);
-
- // Some communal view implementations, such as SurfaceViews, do not behave correctly
- // inheriting the alpha of their parent. Directly set child alpha here to work around
- // this.
- for (int i = mView.getChildCount() - 1; i >= 0; --i) {
- mView.getChildAt(i).setAlpha(alpha);
- }
- }
- }
- @Override
- public void onInit() {
- setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
- setState(STATE_DOZING, mStatusBarStateController.isDozing());
- }
-
- @Override
- protected void onViewAttached() {
- mKeyguardStateController.addCallback(mKeyguardCallback);
- mStatusBarStateController.addCallback(mDozeCallback);
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
- }
-
- @Override
- protected void onViewDetached() {
- mKeyguardStateController.removeCallback(mKeyguardCallback);
- mStatusBarStateController.removeCallback(mDozeCallback);
- mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
- }
-
- private void setState(@State int stateFlag, boolean enabled) {
- final int existingState = mState;
- if (DEBUG) {
- Log.d(TAG, "setState flag:" + describeState(stateFlag) + " enabled:" + enabled);
- }
-
- if (enabled) {
- mState |= stateFlag;
- } else {
- mState &= ~stateFlag;
- }
-
- if (DEBUG) {
- Log.d(TAG, "updated state:" + describeState());
- }
-
- if (existingState != mState) {
- showSource();
- }
-
- updateCommunalViewOccluded();
- }
-
- private String describeState(@State int stateFlag) {
- switch(stateFlag) {
- case STATE_DOZING:
- return "dozing";
- case STATE_BOUNCER_SHOWING:
- return "bouncer_showing";
- case STATE_KEYGUARD_SHOWING:
- return "keyguard_showing";
- default:
- return "UNDEFINED_STATE";
- }
- }
-
- private String describeState() {
- StringBuilder stringBuilder = new StringBuilder();
-
- if ((mState & STATE_KEYGUARD_SHOWING) == STATE_KEYGUARD_SHOWING) {
- stringBuilder.append(String.format(STATE_LIST_FORMAT,
- describeState(STATE_KEYGUARD_SHOWING)));
- }
- if ((mState & STATE_DOZING) == STATE_DOZING) {
- stringBuilder.append(String.format(STATE_LIST_FORMAT,
- describeState(STATE_DOZING)));
- }
- if ((mState & STATE_BOUNCER_SHOWING) == STATE_BOUNCER_SHOWING) {
- stringBuilder.append(String.format(STATE_LIST_FORMAT,
- describeState(STATE_BOUNCER_SHOWING)));
- }
-
- return stringBuilder.toString();
- }
-
- private void showSource() {
- final ShowRequest request = new ShowRequest(
- (mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
- && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
- && mCurrentSource != null,
- mCurrentSource);
-
- if (mLastRequest.isPresent() && Objects.equals(mLastRequest.get(), request)) {
- return;
- }
-
- mLastRequest = Optional.of(request);
-
- // Make sure all necessary states are present for showing communal and all invalid states
- // are absent
- mMainExecutor.execute(() -> {
- if (DEBUG) {
- Log.d(TAG, "showSource. currentSource:" + request.getSource());
- }
-
- if (request.shouldShow()) {
- mView.removeAllViews();
-
- // Make view visible.
- mView.setVisibility(View.VISIBLE);
-
- final Context context = mView.getContext();
-
- final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
- request.getSource().requestCommunalView(context);
-
- if (listenableFuture == null) {
- Log.e(TAG, "could not request communal view");
- return;
- }
-
- listenableFuture.addListener(() -> {
- try {
- final CommunalSource.CommunalViewResult result = listenableFuture.get();
- result.view.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mView.addView(result.view);
-
- mCommunalViewController = result.viewController;
- mCommunalViewController.init();
- } catch (Exception e) {
- Log.e(TAG, "could not obtain communal view through callback:" + e);
- }
- }, mMainExecutor);
- } else {
- mView.removeAllViews();
- mView.setVisibility(View.INVISIBLE);
- mCommunalStateController.setCommunalViewShowing(false);
- }
- });
- }
-
- /**
- * Instructs {@link CommunalHostViewController} to display provided source.
- *
- * @param source The new {@link CommunalSource}, {@code null} if not set.
- */
- public void show(WeakReference<CommunalSource> source) {
- mCurrentSource = source;
- showSource();
- }
-
- /**
- * Update position of the view with an optional animation
- */
- public void updatePosition(int y, boolean animate) {
- PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, COMMUNAL_ANIMATION_PROPERTIES,
- animate);
- }
-
- /**
- * Invoked when the quick settings is expanded.
- * @param expansionFraction the percentage the QS shade has been expanded.
- */
- public void updateQsExpansion(float expansionFraction) {
- mQsExpansion = expansionFraction;
- updateCommunalViewOccluded();
- }
-
- /**
- * Invoked when the main shade is expanded.
- * @param shadeExpansion the percentage the main shade has expanded.
- */
- public void updateShadeExpansion(float shadeExpansion) {
- mShadeExpansion = shadeExpansion;
- updateCommunalViewOccluded();
- }
-
- private void updateCommunalViewOccluded() {
- final boolean bouncerShowing = (mState & STATE_BOUNCER_SHOWING) == STATE_BOUNCER_SHOWING;
- final int statusBarState = mStatusBarStateController.getState();
- final boolean shadeExpanded = statusBarState == StatusBarState.SHADE
- || statusBarState == StatusBarState.SHADE_LOCKED;
-
- mCommunalStateController.setCommunalViewOccluded(
- bouncerShowing || shadeExpanded || mQsExpansion > 0.0f || mShadeExpansion > 0.0f);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
deleted file mode 100644
index 424da0b111c5..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.util.Log;
-
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-
-/**
- * {@link CommunalHostViewPositionAlgorithm} calculates the position of the communal view given
- * input such as the notification panel position.
- */
-public class CommunalHostViewPositionAlgorithm {
- private static final String TAG = "CommunalPositionAlg";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- /**
- * @see NotificationPanelViewController#getExpandedFraction()
- */
- private float mPanelExpansion;
-
- /**
- * Height of {@link CommunalHostView}.
- */
- private int mCommunalHeight;
-
- /**
- * A data container for the result of the position algorithm.
- */
- public static class Result {
- /**
- * The y translation of the clock.
- */
- public int communalY;
- }
-
- /**
- * Sets the conditions under which the result should be calculated from.
- * @param panelExpansion The percentage the keyguard panel has been moved upwards.
- * @param communalHeight The height of the communal panel.
- */
- public void setup(float panelExpansion, int communalHeight) {
- if (DEBUG) {
- Log.d(TAG, "setup. panelExpansion:" + panelExpansion);
- }
- mPanelExpansion = panelExpansion;
- mCommunalHeight = communalHeight;
- }
-
- /**
- * Calculates the position based on factors input through {link {@link #setup(float, int)}}.
- * @param result The resulting calculations.
- */
- public void run(Result result) {
- // The panel expansion relates to the keyguard expansion. At full expansion, the communal
- // view should be aligned at the top (0). Otherwise, it should be shifted offscreen by the
- // unexpanded amount.
- result.communalY = (int) ((1 - mPanelExpansion) * -mCommunalHeight);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
deleted file mode 100644
index 42ecd5c835bb..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.util.ViewController;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Optional;
-
-/**
- * {@link CommunalSource} defines an interface for working with a source for communal data. Clients
- * may request a communal surface that can be shown within a {@link android.view.SurfaceView}.
- * Callbacks may also be registered to listen to state changes.
- */
-public interface CommunalSource {
- /**
- * {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
- */
- interface Connector {
- Connection connect(Connection.Callback callback);
- }
-
- /**
- * {@link Connection} defines an interface for an entity which holds the necessary components
- * for establishing and maintaining a connection to the communal source.
- */
- interface Connection {
- /**
- * {@link Callback} defines an interface for clients to be notified when a source is ready
- */
- interface Callback {
- void onSourceEstablished(Optional<CommunalSource> source);
- void onDisconnected();
- }
-
- void disconnect();
- }
-
- /**
- * The {@link Observer} interface specifies an entity which {@link CommunalSource} listeners
- * can be informed of changes to the source, which will require updating. Note that this deals
- * with changes to the source itself, not content which will be updated through the
- * {@link CommunalSource} interface.
- */
- interface Observer {
- interface Callback {
- void onSourceChanged();
- }
-
- void addCallback(Callback callback);
- void removeCallback(Callback callback);
- }
-
- /**
- * {@link CommunalViewResult} is handed back from {@link #requestCommunalView(Context)} and
- * contains the view to be displayed and its associated controller.
- */
- class CommunalViewResult {
- /**
- * The resulting communal view.
- */
- public final View view;
- /**
- * The controller for the communal view.
- */
- public final ViewController<? extends View> viewController;
-
- /**
- * The default constructor for {@link CommunalViewResult}.
- * @param view The communal view.
- * @param viewController The communal view's controller.
- */
- public CommunalViewResult(View view, ViewController<? extends View> viewController) {
- this.view = view;
- this.viewController = viewController;
- }
- }
-
- /**
- * Requests a communal surface that can be displayed inside {@link CommunalHostView}.
- *
- * @param context The {@link View} {@link Context} to build the resulting view from
- * @return A future that can be listened upon for the resulting {@link CommunalViewResult}. The
- * value will be {@code null} in case of a failure.
- */
- ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
deleted file mode 100644
index 58cf35f2917c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
-
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.condition.Monitor;
-
-import com.google.android.collect.Lists;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * A Monitor for reporting a {@link CommunalSource} presence.
- */
-@SysUISingleton
-public class CommunalSourceMonitor {
- private static final String TAG = "CommunalSourceMonitor";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- // A list of {@link Callback} that have registered to receive updates.
- private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final Monitor mConditionsMonitor;
- private final Executor mExecutor;
-
- private CommunalSource mCurrentSource;
-
- // Whether all conditions for communal mode to show have been met.
- private boolean mAllCommunalConditionsMet = false;
-
- // Whether the class is currently listening for condition changes.
- private boolean mListeningForConditions = false;
-
- private final Monitor.Callback mConditionsCallback =
- allConditionsMet -> {
- if (mAllCommunalConditionsMet != allConditionsMet) {
- if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
-
- mAllCommunalConditionsMet = allConditionsMet;
- executeOnSourceAvailableCallbacks();
- }
- };
-
- @VisibleForTesting
- @Inject
- public CommunalSourceMonitor(@Main Executor executor,
- @Named(COMMUNAL_CONDITIONS) Monitor communalConditionsMonitor) {
- mExecutor = executor;
- mConditionsMonitor = communalConditionsMonitor;
- }
-
- /**
- * Sets the current {@link CommunalSource}, informing any callbacks. Any existing
- * {@link CommunalSource} will be disconnected.
- *
- * @param source The new {@link CommunalSource}.
- */
- public void setSource(CommunalSource source) {
- mCurrentSource = source;
-
- if (mAllCommunalConditionsMet) {
- executeOnSourceAvailableCallbacks();
- }
- }
-
- private void executeOnSourceAvailableCallbacks() {
- mExecutor.execute(() -> {
- // If the new source is valid, inform registered Callbacks of its presence.
- Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
- while (itr.hasNext()) {
- Callback cb = itr.next().get();
- if (cb == null) {
- itr.remove();
- } else {
- cb.onSourceAvailable(
- (mAllCommunalConditionsMet && mCurrentSource != null)
- ? new WeakReference<>(mCurrentSource) : null);
- }
- }
- });
- }
-
- /**
- * Adds a {@link Callback} to receive {@link CommunalSource} updates.
- *
- * @param callback The {@link Callback} to add.
- */
- public void addCallback(Callback callback) {
- mExecutor.execute(() -> {
- mCallbacks.add(new WeakReference<>(callback));
-
- // Inform the callback of any already present CommunalSource.
- if (mAllCommunalConditionsMet && mCurrentSource != null) {
- callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
- }
-
- if (!mListeningForConditions) {
- mConditionsMonitor.addCallback(mConditionsCallback);
- mListeningForConditions = true;
- }
- });
- }
-
- /**
- * Removes the specified {@link Callback} from receive future updates if present.
- *
- * @param callback The {@link Callback} to add.
- */
- public void removeCallback(Callback callback) {
- mExecutor.execute(() -> {
- mCallbacks.removeIf(el -> el.get() == callback);
-
- if (mCallbacks.isEmpty() && mListeningForConditions) {
- mConditionsMonitor.removeCallback(mConditionsCallback);
- mListeningForConditions = false;
- }
- });
- }
-
- /**
- * Interface implemented to be notified when new {@link CommunalSource} become available.
- */
- public interface Callback {
- /**
- * Called when a new {@link CommunalSource} has been registered. This will also be invoked
- * when a {@link Callback} is first registered and a {@link CommunalSource} is already
- * registered.
- *
- * @param source The new {@link CommunalSource}.
- */
- void onSourceAvailable(WeakReference<CommunalSource> source);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
deleted file mode 100644
index f965431a8001..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.time.SystemClock;
-
-import java.util.Optional;
-
-import javax.inject.Inject;
-
-/**
- * The {@link CommunalSourcePrimer} is responsible for priming SystemUI with a pre-configured
- * Communal source. The SystemUI service binds to the component to retrieve the
- * {@link CommunalSource}. {@link CommunalSourcePrimer} has no effect
- * if there is no pre-defined value.
- */
-@SysUISingleton
-public class CommunalSourcePrimer extends CoreStartable {
- private static final String TAG = "CommunalSourcePrimer";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final SystemClock mSystemClock;
- private final DelayableExecutor mMainExecutor;
- private final CommunalSourceMonitor mMonitor;
- private final int mBaseReconnectDelayMs;
- private final int mMaxReconnectAttempts;
- private final int mMinConnectionDuration;
-
- private int mReconnectAttempts = 0;
- private Runnable mCurrentReconnectCancelable;
-
- private final Optional<CommunalSource.Observer> mObserver;
- private final Optional<CommunalSource.Connector> mConnector;
-
- private CommunalSource.Connection mCurrentConnection;
-
- private final Runnable mConnectRunnable = new Runnable() {
- @Override
- public void run() {
- mCurrentReconnectCancelable = null;
- connect();
- }
- };
-
- private final CommunalSource.Observer.Callback mObserverCallback = () -> {
- initiateConnectionAttempt();
- };
-
- @Inject
- public CommunalSourcePrimer(Context context, @Main Resources resources,
- SystemClock clock,
- DelayableExecutor mainExecutor,
- CommunalSourceMonitor monitor,
- Optional<CommunalSource.Connector> connector,
- Optional<CommunalSource.Observer> observer) {
- super(context);
- mSystemClock = clock;
- mMainExecutor = mainExecutor;
- mMonitor = monitor;
- mConnector = connector;
- mObserver = observer;
-
- mMaxReconnectAttempts = resources.getInteger(
- R.integer.config_communalSourceMaxReconnectAttempts);
- mBaseReconnectDelayMs = resources.getInteger(
- R.integer.config_communalSourceReconnectBaseDelay);
- mMinConnectionDuration = resources.getInteger(
- R.integer.config_connectionMinDuration);
- }
-
- @Override
- public void start() {
- }
-
- private void initiateConnectionAttempt() {
- // Reset attempts
- mReconnectAttempts = 0;
- mMonitor.setSource(null);
-
- // The first attempt is always a direct invocation rather than delayed.
- connect();
- }
-
- private void scheduleConnectionAttempt() {
- // always clear cancelable if present.
- if (mCurrentReconnectCancelable != null) {
- mCurrentReconnectCancelable.run();
- mCurrentReconnectCancelable = null;
- }
-
- if (mReconnectAttempts >= mMaxReconnectAttempts) {
- if (DEBUG) {
- Log.d(TAG, "exceeded max connection attempts.");
- }
- return;
- }
-
- final long reconnectDelayMs =
- (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
-
- if (DEBUG) {
- Log.d(TAG,
- "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
- }
-
- mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
- reconnectDelayMs);
-
- mReconnectAttempts++;
- }
-
- @Override
- protected void onBootCompleted() {
- if (mObserver.isPresent()) {
- mObserver.get().addCallback(mObserverCallback);
- }
- initiateConnectionAttempt();
- }
-
- private void connect() {
- if (DEBUG) {
- Log.d(TAG, "attempting to connect to communal source");
- }
-
- if (mCurrentConnection != null) {
- if (DEBUG) {
- Log.d(TAG, "canceling in-flight connection");
- }
- mCurrentConnection.disconnect();
- }
-
- mCurrentConnection = mConnector.get().connect(new CommunalSource.Connection.Callback() {
- private long mStartTime;
-
- @Override
- public void onSourceEstablished(Optional<CommunalSource> optionalSource) {
- mStartTime = mSystemClock.currentTimeMillis();
-
- if (optionalSource.isPresent()) {
- final CommunalSource source = optionalSource.get();
- mMonitor.setSource(source);
- } else {
- scheduleConnectionAttempt();
- }
- }
-
- @Override
- public void onDisconnected() {
- if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
- initiateConnectionAttempt();
- } else {
- scheduleConnectionAttempt();
- }
- }
- });
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
deleted file mode 100644
index c72f5422b1f5..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.policy.CallbackController;
-
-import java.util.ArrayList;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * CommunalStateController enables publishing and listening to communal-related state changes.
- */
-@SysUISingleton
-public class CommunalStateController implements
- CallbackController<CommunalStateController.Callback> {
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private boolean mCommunalViewOccluded;
- private boolean mCommunalViewShowing;
-
- /**
- * Callback for communal events.
- */
- public interface Callback {
- /**
- * Called when the visibility of the communal view changes.
- */
- default void onCommunalViewShowingChanged() {
- }
-
- /**
- * Called when the occlusion of the communal view changes.
- */
- default void onCommunalViewOccludedChanged() {
- }
- }
-
- @VisibleForTesting
- @Inject
- public CommunalStateController() {
- }
-
- /**
- * Sets whether the communal view is showing.
- * @param communalViewShowing {@code true} if the view is showing, {@code false} otherwise.
- */
- public void setCommunalViewShowing(boolean communalViewShowing) {
- if (mCommunalViewShowing == communalViewShowing) {
- return;
- }
-
- mCommunalViewShowing = communalViewShowing;
-
- final ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
- for (Callback callback : callbacks) {
- callback.onCommunalViewShowingChanged();
- }
- }
-
- /**
- * Sets whether the communal view is occluded (but otherwise still showing).
- * @param communalViewOccluded {@code true} if the view is occluded, {@code false} otherwise.
- */
- public void setCommunalViewOccluded(boolean communalViewOccluded) {
- if (mCommunalViewOccluded == communalViewOccluded) {
- return;
- }
-
- mCommunalViewOccluded = communalViewOccluded;
-
- ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).onCommunalViewOccludedChanged();
- }
- }
-
- /**
- * Returns whether the communal view is showing.
- * @return {@code true} if the view is showing, {@code false} otherwise.
- */
- public boolean getCommunalViewShowing() {
- return mCommunalViewShowing;
- }
-
- /**
- * Returns whether the communal view is occluded.
- * @return {@code true} if the view is occluded, {@code false} otherwise.
- */
- public boolean getCommunalViewOccluded() {
- return mCommunalViewOccluded;
- }
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- if (!mCallbacks.contains(callback)) {
- mCallbacks.add(callback);
- }
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.remove(callback);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/PackageObserver.java b/packages/SystemUI/src/com/android/systemui/communal/PackageObserver.java
deleted file mode 100644
index 3d25d126a291..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/PackageObserver.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.PatternMatcher;
-import android.util.Log;
-
-import com.google.android.collect.Lists;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-/**
- * {@link PackageObserver} allows for monitoring the system for changes relating to a particular
- * package. This can be used by {@link CommunalSource} clients to detect when a related package
- * has changed and reloading is necessary.
- */
-public class PackageObserver implements CommunalSource.Observer {
- private static final String TAG = "PackageObserver";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Log.d(TAG, "package added receiver - onReceive");
- }
-
- final Iterator<WeakReference<Callback>> iter = mCallbacks.iterator();
- while (iter.hasNext()) {
- final Callback callback = iter.next().get();
- if (callback != null) {
- callback.onSourceChanged();
- } else {
- iter.remove();
- }
- }
- }
- };
-
- private final String mPackageName;
- private final Context mContext;
-
- public PackageObserver(Context context, String packageName) {
- mContext = context;
- mPackageName = packageName;
- }
-
- @Override
- public void addCallback(Callback callback) {
- if (DEBUG) {
- Log.d(TAG, "addCallback:" + callback);
- }
- mCallbacks.add(new WeakReference<>(callback));
-
- // Only register for listening to package additions on first callback.
- if (mCallbacks.size() > 1) {
- return;
- }
-
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addDataScheme("package");
- filter.addDataSchemeSpecificPart(mPackageName, PatternMatcher.PATTERN_LITERAL);
- // Note that we directly register the receiver here as data schemes are not supported by
- // BroadcastDispatcher.
- mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
- }
-
- @Override
- public void removeCallback(Callback callback) {
- if (DEBUG) {
- Log.d(TAG, "removeCallback:" + callback);
- }
- final boolean removed = mCallbacks.removeIf(el -> el.get() == callback);
-
- if (removed && mCallbacks.isEmpty()) {
- mContext.unregisterReceiver(mReceiver);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
deleted file mode 100644
index 25519d0f96c7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.conditions;
-
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import androidx.annotation.MainThread;
-
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.settings.SecureSettings;
-
-import javax.inject.Inject;
-
-/**
- * Monitors the communal setting, and informs any listeners with updates.
- */
-public class CommunalSettingCondition extends Condition {
- private final SecureSettings mSecureSettings;
- private final ContentObserver mCommunalSettingContentObserver;
-
- @Inject
- public CommunalSettingCondition(@MainThread Handler mainHandler,
- SecureSettings secureSettings) {
- mSecureSettings = secureSettings;
-
- mCommunalSettingContentObserver = new ContentObserver(mainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final boolean communalSettingEnabled = mSecureSettings.getIntForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED, 0, UserHandle.USER_SYSTEM) == 1;
- updateCondition(communalSettingEnabled);
- }
- };
- }
-
- @Override
- protected void start() {
- mSecureSettings.registerContentObserverForUser(Settings.Secure.COMMUNAL_MODE_ENABLED,
- false /*notifyForDescendants*/, mCommunalSettingContentObserver,
- UserHandle.USER_SYSTEM);
-
- // Fetches setting immediately.
- mCommunalSettingContentObserver.onChange(false);
- }
-
- @Override
- protected void stop() {
- mSecureSettings.unregisterContentObserver(mCommunalSettingContentObserver);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
deleted file mode 100644
index 814b251d08ab..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.dagger;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.R;
-import com.android.systemui.communal.CommunalSource;
-import com.android.systemui.communal.PackageObserver;
-import com.android.systemui.communal.conditions.CommunalSettingCondition;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.condition.Monitor;
-import com.android.systemui.util.condition.dagger.MonitorComponent;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.StringKey;
-
-/**
- * Dagger Module providing Communal-related functionality.
- */
-@Module(subcomponents = {
- CommunalViewComponent.class,
-})
-public interface CommunalModule {
- String COMMUNAL_CONDITIONS = "communal_conditions";
-
- /** */
- @Provides
- static Optional<CommunalSource.Observer> provideCommunalSourcePackageObserver(
- Context context, @Main Resources resources) {
- final String componentName = resources.getString(R.string.config_communalSourceComponent);
-
- if (TextUtils.isEmpty(componentName)) {
- return Optional.empty();
- }
-
- return Optional.of(new PackageObserver(context,
- ComponentName.unflattenFromString(componentName).getPackageName()));
- }
-
- /**
- * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
- */
- @Provides
- @ElementsIntoSet
- @Named(COMMUNAL_CONDITIONS)
- static Set<Condition> provideCommunalConditions(
- CommunalSettingCondition communalSettingCondition) {
- return new HashSet<>(Collections.singletonList(communalSettingCondition));
- }
-
- /**
- * TODO(b/205638389): Remove when there is a base implementation of
- * {@link CommunalSource.Connector}. Currently a place holder to allow a map to be present.
- */
- @Provides
- @IntoMap
- @Nullable
- @StringKey("empty")
- static CommunalSource.Connector provideEmptyCommunalSourceConnector() {
- return null;
- }
-
- /** */
- @Provides
- static Optional<CommunalSource.Connector> provideCommunalSourceConnector(
- @Main Resources resources,
- Map<Class<?>, Provider<CommunalSource.Connector>> connectorCreators) {
- final String className = resources.getString(R.string.config_communalSourceConnector);
-
- if (TextUtils.isEmpty(className)) {
- return Optional.empty();
- }
-
- try {
- Class<?> clazz = Class.forName(className);
- Provider<CommunalSource.Connector> provider = connectorCreators.get(clazz);
- return provider != null ? Optional.of(provider.get()) : Optional.empty();
- } catch (ClassNotFoundException e) {
- return Optional.empty();
- }
- }
-
- /** */
- @Provides
- @Named(COMMUNAL_CONDITIONS)
- static Monitor provideCommunalSourceMonitor(
- @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions,
- MonitorComponent.Factory factory) {
- final MonitorComponent component = factory.create(communalConditions, new HashSet<>());
- return component.getMonitor();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java
deleted file mode 100644
index 3a80a03aecb2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.dagger;
-
-import com.android.systemui.communal.CommunalHostView;
-import com.android.systemui.communal.CommunalHostViewController;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Subcomponent for working with {@link CommunalHostView}.
- */
-@Subcomponent
-public interface CommunalViewComponent {
- /** Simple factory for {@link CommunalViewComponent}. */
- @Subcomponent.Factory
- interface Factory {
- CommunalViewComponent build(@BindsInstance CommunalHostView view);
- }
-
- /** Builds a {@link CommunalHostViewController}. */
- CommunalHostViewController getCommunalHostViewController();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 27993010c917..166c2654cbaa 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -32,7 +32,6 @@ import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.UdfpsHbmProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.classifier.FalsingModule;
-import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
@@ -49,6 +48,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
+import com.android.systemui.smartspace.dagger.SmartspaceModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -66,8 +66,8 @@ import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -106,7 +106,6 @@ import dagger.Provides;
AssistModule.class,
BiometricsModule.class,
ClockModule.class,
- CommunalModule.class,
DreamModule.class,
ControlsModule.class,
DemoModeModule.class,
@@ -121,6 +120,7 @@ import dagger.Provides;
SettingsModule.class,
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
+ SmartspaceModule.class,
StatusBarPolicyModule.class,
StatusBarWindowModule.class,
SysUIConcurrencyModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index ebc766635733..dfbb0c7c1624 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
+ // True if the service has been destroyed.
+ private boolean mDestroyed;
+
private final Complication.Host mHost = new Complication.Host() {
@Override
public void requestExitDream() {
@@ -134,6 +137,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mPreviewComplication.setDreamLabel(null);
mStateController.removeComplication(mPreviewComplication);
mStateController.setPreviewMode(false);
+ mDestroyed = true;
super.onDestroy();
}
@@ -141,6 +145,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
setCurrentState(Lifecycle.State.STARTED);
mExecutor.execute(() -> {
+ if (mDestroyed) {
+ // The task could still be executed after the service has been destroyed. Bail if
+ // that is the case.
+ return;
+ }
mStateController.setShouldShowComplications(shouldShowComplications());
mStateController.setPreviewMode(isPreviewMode());
if (isPreviewMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
index a5dcd39264df..a83e006dfa2f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams;
import android.content.Context;
+import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -25,7 +26,10 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.complication.ComplicationLayoutParams;
import com.android.systemui.dreams.complication.ComplicationViewModel;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+import com.android.systemui.dreams.smartspace.DreamsSmartspaceController;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+
+import java.util.List;
import javax.inject.Inject;
@@ -39,10 +43,22 @@ public class SmartSpaceComplication implements Complication {
* SystemUI.
*/
public static class Registrant extends CoreStartable {
- private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamsSmartspaceController mSmartSpaceController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final SmartSpaceComplication mComplication;
+ private final BcSmartspaceDataPlugin.SmartspaceTargetListener mSmartspaceListener =
+ new BcSmartspaceDataPlugin.SmartspaceTargetListener() {
+ @Override
+ public void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets) {
+ if (!targets.isEmpty()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ } else {
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ }
+ };
+
/**
* Default constructor for {@link SmartSpaceComplication}.
*/
@@ -50,7 +66,7 @@ public class SmartSpaceComplication implements Complication {
public Registrant(Context context,
DreamOverlayStateController dreamOverlayStateController,
SmartSpaceComplication smartSpaceComplication,
- LockscreenSmartspaceController smartSpaceController) {
+ DreamsSmartspaceController smartSpaceController) {
super(context);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = smartSpaceComplication;
@@ -59,32 +75,27 @@ public class SmartSpaceComplication implements Complication {
@Override
public void start() {
- addOrRemoveOverlay();
mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
@Override
public void onStateChanged() {
- addOrRemoveOverlay();
+ if (mDreamOverlayStateController.isOverlayActive()) {
+ mSmartSpaceController.addListener(mSmartspaceListener);
+ } else {
+ mSmartSpaceController.removeListener(mSmartspaceListener);
+ }
}
});
}
-
- private void addOrRemoveOverlay() {
- if (mDreamOverlayStateController.isPreviewMode()) {
- mDreamOverlayStateController.removeComplication(mComplication);
- } else if (mSmartSpaceController.isEnabled()) {
- mDreamOverlayStateController.addComplication(mComplication);
- }
- }
}
private static class SmartSpaceComplicationViewHolder implements ViewHolder {
private static final int SMARTSPACE_COMPLICATION_WEIGHT = 10;
- private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamsSmartspaceController mSmartSpaceController;
private final Context mContext;
protected SmartSpaceComplicationViewHolder(
Context context,
- LockscreenSmartspaceController smartSpaceController) {
+ DreamsSmartspaceController smartSpaceController) {
mSmartSpaceController = smartSpaceController;
mContext = context;
}
@@ -109,12 +120,12 @@ public class SmartSpaceComplication implements Complication {
}
}
- private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamsSmartspaceController mSmartSpaceController;
private final Context mContext;
@Inject
public SmartSpaceComplication(Context context,
- LockscreenSmartspaceController smartSpaceController) {
+ DreamsSmartspaceController smartSpaceController) {
mContext = context;
mSmartSpaceController = smartSpaceController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index 62b319bd9e97..4e528bbe9386 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -21,6 +21,7 @@ import static com.android.systemui.dreams.complication.dagger.ComplicationModule
import android.graphics.Rect;
import android.graphics.Region;
+import android.util.Log;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -42,6 +43,8 @@ import javax.inject.Named;
* a {@link ComplicationLayoutEngine}.
*/
public class ComplicationHostViewController extends ViewController<ConstraintLayout> {
+ public static final String TAG = "ComplicationHostViewController";
+
private final ComplicationLayoutEngine mLayoutEngine;
private final LifecycleOwner mLifecycleOwner;
private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
@@ -113,6 +116,12 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
final Complication.ViewHolder viewHolder = complication.getComplication()
.createView(complication);
mComplications.put(id, viewHolder);
+ if (viewHolder.getView().getParent() != null) {
+ Log.e(TAG, "View for complication "
+ + complication.getComplication().getClass()
+ + " already has a parent. Make sure not to reuse complication "
+ + "views!");
+ }
mLayoutEngine.addComplication(id, viewHolder.getView(),
viewHolder.getLayoutParams(), viewHolder.getCategory());
});
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 de5b4bb9e520..c7b02cd00e96 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -26,7 +26,7 @@ import dagger.Module;
import dagger.Provides;
/**
- * Dagger Module providing Communal-related functionality.
+ * Dagger Module providing Dream-related functionality.
*/
@Module(includes = {
RegisteredComplicationsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
new file mode 100644
index 000000000000..4e228a14fc88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.smartspace
+
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.content.Context
+import android.graphics.Color
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.smartspace.SmartspacePrecondition
+import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER
+import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
+import com.android.systemui.util.concurrency.Execution
+import java.lang.RuntimeException
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Controller for managing the smartspace view on the dream
+ */
+@SysUISingleton
+class DreamsSmartspaceController @Inject constructor(
+ private val context: Context,
+ private val smartspaceManager: SmartspaceManager,
+ private val execution: Execution,
+ @Main private val uiExecutor: Executor,
+ private val smartspaceViewComponentFactory: SmartspaceViewComponent.Factory,
+ @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
+ @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ private val optionalTargetFilter: Optional<SmartspaceTargetFilter>,
+ @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>
+) {
+ companion object {
+ private const val TAG = "DreamsSmartspaceCtrlr"
+ }
+
+ private var session: SmartspaceSession? = null
+ private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
+
+ // A shadow copy of listeners is maintained to track whether the session should remain open.
+ private var listeners = mutableSetOf<BcSmartspaceDataPlugin.SmartspaceTargetListener>()
+
+ // Smartspace can be used on multiple displays, such as when the user casts their screen
+ private var smartspaceViews = mutableSetOf<SmartspaceView>()
+
+ var preconditionListener = object : SmartspacePrecondition.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
+ }
+
+ init {
+ precondition.addListener(preconditionListener)
+ }
+
+ var filterListener = object : SmartspaceTargetFilter.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
+ }
+
+ init {
+ targetFilter?.addListener(filterListener)
+ }
+
+ var stateChangeListener = object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ val view = v as SmartspaceView
+ // Until there is dream color matching
+ view.setPrimaryTextColor(Color.WHITE)
+ smartspaceViews.add(view)
+ connectSession()
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ smartspaceViews.remove(v as SmartspaceView)
+
+ if (smartspaceViews.isEmpty()) {
+ disconnect()
+ }
+ }
+ }
+
+ private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+
+ val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ /**
+ * Constructs the smartspace view and connects it to the smartspace service.
+ */
+ fun buildAndConnectView(parent: ViewGroup): View? {
+ execution.assertIsMainThread()
+
+ if (!precondition.conditionsMet()) {
+ throw RuntimeException("Cannot build view when not enabled")
+ }
+
+ val view = buildView(parent)
+ connectSession()
+
+ return view
+ }
+
+ private fun buildView(parent: ViewGroup): View? {
+ return if (plugin != null) {
+ var view = smartspaceViewComponentFactory.create(parent, plugin, stateChangeListener)
+ .getView()
+
+ if (view is View) {
+ return view
+ }
+
+ return null
+ } else {
+ null
+ }
+ }
+
+ private fun hasActiveSessionListeners(): Boolean {
+ return smartspaceViews.isNotEmpty() || listeners.isNotEmpty()
+ }
+
+ private fun connectSession() {
+ if (plugin == null || session != null || !hasActiveSessionListeners()) {
+ return
+ }
+
+ if (!precondition.conditionsMet()) {
+ return
+ }
+
+ // TODO(b/217559844): Replace with "dream" session when available.
+ val newSession = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, "lockscreen").build())
+ Log.d(TAG, "Starting smartspace session for dream")
+ newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ this.session = newSession
+
+ plugin.registerSmartspaceEventNotifier {
+ e -> session?.notifySmartspaceEvent(e)
+ }
+
+ reloadSmartspace()
+ }
+
+ /**
+ * Disconnects the smartspace view from the smartspace service and cleans up any resources.
+ */
+ private fun disconnect() {
+ if (hasActiveSessionListeners()) return
+
+ execution.assertIsMainThread()
+
+ if (session == null) {
+ return
+ }
+
+ session?.let {
+ it.removeOnTargetsAvailableListener(sessionListener)
+ it.close()
+ }
+
+ session = null
+
+ plugin?.registerSmartspaceEventNotifier(null)
+ plugin?.onTargetsAvailable(emptyList())
+ Log.d(TAG, "Ending smartspace session for dream")
+ }
+
+ fun addListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.registerListener(listener)
+ listeners.add(listener)
+
+ connectSession()
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.unregisterListener(listener)
+ listeners.remove(listener)
+ disconnect()
+ }
+
+ private fun reloadSmartspace() {
+ session?.requestSmartspaceUpdate()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index bf689e30d2fd..b590412dd48c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -155,7 +155,7 @@ public class Flags {
// 1100 - windowing
public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
- new SysPropBooleanFlag(1100, "persist.debug.shell_transit", false);
+ new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 7a278f786a67..af553c744311 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -624,7 +624,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
addIfShouldShowAction(tempActions, new LogoutAction());
}
} else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
- addIfShouldShowAction(tempActions, new EmergencyDialerAction());
+ if (shouldDisplayEmergency()) {
+ addIfShouldShowAction(tempActions, new EmergencyDialerAction());
+ }
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
@@ -704,6 +706,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@VisibleForTesting
+ boolean shouldDisplayEmergency() {
+ // Emergency calling requires a telephony radio.
+ return mHasTelephony;
+ }
+
+ @VisibleForTesting
boolean shouldDisplayBugReport(UserInfo currentUser) {
return mGlobalSettings.getInt(Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0
&& (currentUser == null || currentUser.isPrimary());
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 f16ca7dd7361..0c202e09b62e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -30,12 +30,16 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.media.INearbyMediaDevicesUpdateCallback;
import android.media.MediaMetadata;
import android.media.MediaRoute2Info;
+import android.media.NearbyDevice;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -61,6 +65,7 @@ import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.monet.ColorScheme;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -70,6 +75,9 @@ import com.android.systemui.statusbar.phone.ShadeController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
@@ -77,7 +85,8 @@ import javax.inject.Inject;
/**
* Controller for media output dialog
*/
-public class MediaOutputController implements LocalMediaManager.DeviceCallback {
+public class MediaOutputController implements LocalMediaManager.DeviceCallback,
+ INearbyMediaDevicesUpdateCallback {
private static final String TAG = "MediaOutputController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -95,6 +104,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
private final CommonNotifCollection mNotifCollection;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
+ private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
+ private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>();
private MediaController mMediaController;
@VisibleForTesting
@@ -116,7 +127,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
CommonNotifCollection notifCollection, UiEventLogger uiEventLogger,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator,
+ Optional<NearbyMediaDevicesManager> nearbyMediaDevicesManagerOptional) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -130,6 +142,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null);
mColorActiveItem = Utils.getColorStateListDefaultColor(mContext,
R.color.media_dialog_active_item_main_content);
mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext,
@@ -144,6 +157,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
void start(@NonNull Callback cb) {
mMediaDevices.clear();
+ mNearbyDeviceInfoMap.clear();
+ if (mNearbyMediaDevicesManager != null) {
+ mNearbyMediaDevicesManager.registerNearbyDevicesCallback(this);
+ }
if (!TextUtils.isEmpty(mPackageName)) {
for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
@@ -187,6 +204,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
mLocalMediaManager.stopScan();
}
mMediaDevices.clear();
+ if (mNearbyMediaDevicesManager != null) {
+ mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
+ }
+ mNearbyDeviceInfoMap.clear();
}
@Override
@@ -417,6 +438,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
}
mMediaDevices.clear();
mMediaDevices.addAll(targetMediaDevices);
+ attachRangeInfo();
+ }
+
+ private void attachRangeInfo() {
+ for (MediaDevice mediaDevice : mMediaDevices) {
+ if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
+ mediaDevice.setRangeZone(mNearbyDeviceInfoMap.get(mediaDevice.getId()));
+ }
+ }
}
List<MediaDevice> getGroupMediaDevices() {
@@ -604,7 +634,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
// We show the output group dialog from the output dialog.
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
- mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
+ mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
+
MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
controller);
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
@@ -629,6 +661,20 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
|| mLocalMediaManager.isMediaSessionAvailableForVolumeControl();
}
+ @Override
+ public void onDevicesUpdated(List<NearbyDevice> nearbyDevices) throws RemoteException {
+ mNearbyDeviceInfoMap.clear();
+ for (NearbyDevice nearbyDevice : nearbyDevices) {
+ mNearbyDeviceInfoMap.put(nearbyDevice.getMediaRoute2Id(), nearbyDevice.getRangeZone());
+ }
+ mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
private final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 9e252ea1eddc..a7e54801bf47 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -22,9 +22,11 @@ import android.view.View
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.phone.ShadeController
+import java.util.Optional
import javax.inject.Inject
/**
@@ -38,7 +40,8 @@ class MediaOutputDialogFactory @Inject constructor(
private val starter: ActivityStarter,
private val notifCollection: CommonNotifCollection,
private val uiEventLogger: UiEventLogger,
- private val dialogLaunchAnimator: DialogLaunchAnimator
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -50,8 +53,8 @@ class MediaOutputDialogFactory @Inject constructor(
mediaOutputDialog?.dismiss()
val controller = MediaOutputController(context, packageName, aboveStatusBar,
- mediaSessionManager, lbm, shadeController, starter, notifCollection,
- uiEventLogger, dialogLaunchAnimator)
+ mediaSessionManager, lbm, shadeController, starter, notifCollection,
+ uiEventLogger, dialogLaunchAnimator, nearbyMediaDevicesManagerOptional)
val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
mediaOutputDialog = dialog
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 9dd8222ff6a1..3961f079748b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer
+import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
import android.media.MediaRoute2Info
@@ -23,16 +24,12 @@ import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
-import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
-import com.android.systemui.media.taptotransfer.sender.TransferFailed
-import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
-import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
-import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
-import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
+import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
+import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
+import java.lang.IllegalArgumentException
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -46,28 +43,6 @@ class MediaTttCommandLineHelper @Inject constructor(
private val context: Context,
@Main private val mainExecutor: Executor
) {
- /**
- * A map from a display state string typed in the command line to the display int it represents.
- */
- private val stateStringToStateInt: Map<String, Int> = mapOf(
- AlmostCloseToStartCast::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
- AlmostCloseToEndCast::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
- TransferToReceiverTriggered::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
- TransferToThisDeviceTriggered::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
- TransferToReceiverSucceeded::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
- TransferToThisDeviceSucceeded::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
- TransferFailed::class.simpleName!!
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
- FAR_FROM_RECEIVER_STATE
- to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER
- )
-
init {
commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
@@ -76,21 +51,23 @@ class MediaTttCommandLineHelper @Inject constructor(
/** All commands for the sender device. */
inner class SenderCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- val routeInfo = MediaRoute2Info.Builder("id", args[0])
- .addFeature("feature")
- .setPackageName(TEST_PACKAGE_NAME)
- .build()
-
val commandName = args[1]
@StatusBarManager.MediaTransferSenderState
- val displayState = stateStringToStateInt[commandName]
- if (displayState == null) {
+ val displayState: Int?
+ try {
+ displayState = ChipStateSender.getSenderStateIdFromName(commandName)
+ } catch (ex: IllegalArgumentException) {
pw.println("Invalid command name $commandName")
return
}
+ @SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
+ val routeInfo = MediaRoute2Info.Builder("id", args[0])
+ .addFeature("feature")
+ .setPackageName(TEST_PACKAGE_NAME)
+ .build()
statusBarManager.updateMediaTapToTransferSenderDisplay(
displayState,
routeInfo,
@@ -136,6 +113,17 @@ class MediaTttCommandLineHelper @Inject constructor(
/** All commands for the receiver device. */
inner class ReceiverCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
+ val commandName = args[0]
+ @StatusBarManager.MediaTransferReceiverState
+ val displayState: Int?
+ try {
+ displayState = ChipStateReceiver.getReceiverStateIdFromName(commandName)
+ } catch (ex: IllegalArgumentException) {
+ pw.println("Invalid command name $commandName")
+ return
+ }
+
+ @SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
@@ -143,24 +131,12 @@ class MediaTttCommandLineHelper @Inject constructor(
.setPackageName(TEST_PACKAGE_NAME)
.build()
- when(val commandName = args[0]) {
- CLOSE_TO_SENDER_STATE ->
- statusBarManager.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
- routeInfo,
- null,
- null
- )
- FAR_FROM_SENDER_STATE ->
- statusBarManager.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
- routeInfo,
- null,
- null
- )
- else ->
- pw.println("Invalid command name $commandName")
- }
+ statusBarManager.updateMediaTapToTransferReceiverDisplay(
+ displayState,
+ routeInfo,
+ null,
+ null
+ )
}
override fun help(pw: PrintWriter) {
@@ -173,11 +149,5 @@ class MediaTttCommandLineHelper @Inject constructor(
const val SENDER_COMMAND = "media-ttt-chip-sender"
@VisibleForTesting
const val RECEIVER_COMMAND = "media-ttt-chip-receiver"
-@VisibleForTesting
-const val FAR_FROM_RECEIVER_STATE = "FarFromReceiver"
-@VisibleForTesting
-const val CLOSE_TO_SENDER_STATE = "CloseToSender"
-@VisibleForTesting
-const val FAR_FROM_SENDER_STATE = "FarFromSender"
private const val CLI_TAG = "MediaTransferCli"
private const val TEST_PACKAGE_NAME = "com.android.systemui"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
new file mode 100644
index 000000000000..3cc99a8ef77e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.common
+
+/**
+ * A superclass chip state that will be subclassed by the sender chip and receiver chip.
+ */
+interface ChipInfoCommon {
+ /**
+ * Returns the amount of time the given chip state should display on the screen before it times
+ * out and disappears.
+ */
+ fun getTimeoutMs(): Long
+}
+
+const val DEFAULT_TIMEOUT_MILLIS = 3000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 9c4b39d9cb77..71cacac7f4df 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -19,14 +19,18 @@ package com.android.systemui.media.taptotransfer.common
import android.annotation.LayoutRes
import android.annotation.SuppressLint
import android.content.Context
+import android.content.pm.PackageManager
import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+import android.os.PowerManager
+import android.os.SystemClock
+import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
@@ -40,14 +44,18 @@ import com.android.systemui.util.view.ViewUtil
*
* Subclasses need to override and implement [updateChipView], which is where they can control what
* gets displayed to the user.
+ *
+ * The generic type T is expected to contain all the information necessary for the subclasses to
+ * display the chip in a certain state, since they receive <T> in [updateChipView].
*/
-abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
+abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
internal val context: Context,
internal val logger: MediaTttLogger,
private val windowManager: WindowManager,
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
private val tapGestureDetector: TapGestureDetector,
+ private val powerManager: PowerManager,
@LayoutRes private val chipLayoutRes: Int
) {
/** The window layout parameters we'll use when attaching the view to a window. */
@@ -64,10 +72,10 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
}
/** The chip view currently being displayed. Null if the chip is not being displayed. */
- var chipView: ViewGroup? = null
+ private var chipView: ViewGroup? = null
/** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
- var cancelChipViewTimeout: Runnable? = null
+ private var cancelChipViewTimeout: Runnable? = null
/**
* Displays the chip with the current state.
@@ -75,7 +83,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
* This method handles inflating and attaching the view, then delegates to [updateChipView] to
* display the correct information in the chip.
*/
- fun displayChip(chipState: T) {
+ fun displayChip(chipInfo: T) {
val oldChipView = chipView
if (chipView == null) {
chipView = LayoutInflater
@@ -84,19 +92,25 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
}
val currentChipView = chipView!!
- updateChipView(chipState, currentChipView)
+ updateChipView(chipInfo, currentChipView)
// Add view if necessary
if (oldChipView == null) {
tapGestureDetector.addOnGestureDetectedCallback(TAG, this::onScreenTapped)
windowManager.addView(chipView, windowLayoutParams)
+ // Wake the screen so the user will see the chip
+ powerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION,
+ "com.android.systemui:media_tap_to_transfer_activated"
+ )
}
// Cancel and re-set the chip timeout each time we get a new state.
cancelChipViewTimeout?.run()
cancelChipViewTimeout = mainExecutor.executeDelayed(
{ removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
- chipState.getTimeoutMs()
+ chipInfo.getTimeoutMs()
)
}
@@ -117,20 +131,28 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
}
/**
- * A method implemented by subclasses to update [currentChipView] based on [chipState].
+ * A method implemented by subclasses to update [currentChipView] based on [chipInfo].
*/
- abstract fun updateChipView(chipState: T, currentChipView: ViewGroup)
+ abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup)
/**
* An internal method to set the icon on the view.
*
* This is in the common superclass since both the sender and the receiver show an icon.
+ *
+ * @param appPackageName the package name of the app playing the media. Will be used to fetch
+ * the app icon and app name if overrides aren't provided.
*/
- internal fun setIcon(chipState: T, currentChipView: ViewGroup) {
+ internal fun setIcon(
+ currentChipView: ViewGroup,
+ appPackageName: String?,
+ appIconDrawableOverride: Drawable? = null,
+ appNameOverride: CharSequence? = null,
+ ) {
val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
- appIconView.contentDescription = chipState.getAppName(context)
+ appIconView.contentDescription = appNameOverride ?: getAppName(appPackageName)
- val appIcon = chipState.getAppIcon(context)
+ val appIcon = appIconDrawableOverride ?: getAppIcon(appPackageName)
val visibility = if (appIcon != null) {
View.VISIBLE
} else {
@@ -140,6 +162,30 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
appIconView.visibility = visibility
}
+ /** Returns the icon of the app playing the media or null if we can't find it. */
+ private fun getAppIcon(appPackageName: String?): Drawable? {
+ appPackageName ?: return null
+ return try {
+ context.packageManager.getApplicationIcon(appPackageName)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Cannot find icon for package $appPackageName", e)
+ null
+ }
+ }
+
+ /** Returns the name of the app playing the media or null if we can't find it. */
+ private fun getAppName(appPackageName: String?): String? {
+ appPackageName ?: return null
+ return try {
+ context.packageManager.getApplicationInfo(
+ appPackageName, PackageManager.ApplicationInfoFlags.of(0)
+ ).loadLabel(context.packageManager).toString()
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Cannot find name for package $appPackageName", e)
+ null
+ }
+ }
+
private fun onScreenTapped(e: MotionEvent) {
val view = chipView ?: return
// If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the
@@ -159,4 +205,3 @@ object MediaTttRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
deleted file mode 100644
index 6f6018170f98..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.taptotransfer.common
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.util.Log
-
-/**
- * A superclass chip state that will be subclassed by the sender chip and receiver chip.
- *
- * @property appPackageName the package name of the app playing the media. Will be used to fetch the
- * app icon and app name.
- */
-open class MediaTttChipState(
- internal val appPackageName: String?,
-) {
- open fun getAppIcon(context: Context): Drawable? {
- appPackageName ?: return null
- return try {
- context.packageManager.getApplicationIcon(appPackageName)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Cannot find icon for package $appPackageName", e)
- null
- }
- }
-
- /** Returns the name of the app playing the media or null if we can't find it. */
- open fun getAppName(context: Context): String? {
- appPackageName ?: return null
- return try {
- context.packageManager.getApplicationInfo(
- appPackageName, PackageManager.ApplicationInfoFlags.of(0)
- ).loadLabel(context.packageManager).toString()
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Cannot find name for package $appPackageName", e)
- null
- }
- }
-
- /**
- * Returns the amount of time this chip should display on the screen before it times out and
- * disappears. [MediaTttChipControllerCommon] will ensure that the timeout resets each time we
- * receive a new state.
- */
- open fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
-}
-
-private const val DEFAULT_TIMEOUT_MILLIS = 3000L
-private val TAG = MediaTttChipState::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
index 6a4b62a8c1ae..a0e803f6bb8d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
@@ -16,35 +16,43 @@
package com.android.systemui.media.taptotransfer.receiver
-import android.content.Context
-import android.graphics.drawable.Drawable
-import com.android.systemui.media.taptotransfer.common.MediaTttChipState
+import android.app.StatusBarManager
+import com.android.internal.logging.UiEventLogger
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip on
* the receiver device.
- *
- * @property appIconDrawable a drawable representing the icon of the app playing the media. If
- * present, this will be used in [this.getAppIcon] instead of [appPackageName].
- * @property appName a name for the app playing the media. If present, this will be used in
- * [this.getAppName] instead of [appPackageName].
*/
-class ChipStateReceiver(
- appPackageName: String?,
- private val appIconDrawable: Drawable?,
- private val appName: CharSequence?
-) : MediaTttChipState(appPackageName) {
- override fun getAppIcon(context: Context): Drawable? {
- if (appIconDrawable != null) {
- return appIconDrawable
- }
- return super.getAppIcon(context)
- }
+enum class ChipStateReceiver(
+ @StatusBarManager.MediaTransferSenderState val stateInt: Int,
+ val uiEvent: UiEventLogger.UiEventEnum,
+) {
+ CLOSE_TO_SENDER(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER,
+ ),
+ FAR_FROM_SENDER(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER,
+ );
+
+ companion object {
+ /**
+ * Returns the receiver state enum associated with the given [displayState] from
+ * [StatusBarManager].
+ */
+ fun getReceiverStateFromId(
+ @StatusBarManager.MediaTransferReceiverState displayState: Int
+ ) : ChipStateReceiver = values().first { it.stateInt == displayState }
+
- override fun getAppName(context: Context): String? {
- if (appName != null) {
- return appName.toString()
- }
- return super.getAppName(context)
+ /**
+ * Returns the state int from [StatusBarManager] associated with the given sender state
+ * name.
+ *
+ * @param name the name of one of the [ChipStateReceiver] enums.
+ */
+ @StatusBarManager.MediaTransferReceiverState
+ fun getReceiverStateIdFromName(name: String): Int = valueOf(name).stateInt
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 1a96ddf69b28..44965d705802 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -18,15 +18,19 @@ package com.android.systemui.media.taptotransfer.receiver
import android.app.StatusBarManager
import android.content.Context
+import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
import android.os.Handler
+import android.os.PowerManager
import android.util.Log
import android.view.ViewGroup
import android.view.WindowManager
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
+import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
@@ -49,14 +53,17 @@ class MediaTttChipControllerReceiver @Inject constructor(
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
+ powerManager: PowerManager,
@Main private val mainHandler: Handler,
-) : MediaTttChipControllerCommon<ChipStateReceiver>(
+ private val uiEventLogger: MediaTttReceiverUiEventLogger,
+) : MediaTttChipControllerCommon<ChipReceiverInfo>(
context,
logger,
windowManager,
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip_receiver
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
@@ -82,45 +89,52 @@ class MediaTttChipControllerReceiver @Inject constructor(
appIcon: Icon?,
appName: CharSequence?
) {
- logger.logStateChange(stateIntToString(displayState), routeInfo.id)
- when(displayState) {
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> {
- val packageName = routeInfo.packageName
- if (appIcon == null) {
- displayChip(ChipStateReceiver(packageName, null, appName))
- } else {
- appIcon.loadDrawableAsync(
- context,
- Icon.OnDrawableLoadedListener { drawable ->
- displayChip(
- ChipStateReceiver(packageName, drawable, appName)
- )},
- // Notify the listener on the main handler since the listener will update
- // the UI.
- mainHandler
- )
- }
- }
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER ->
- removeChip(removalReason = FAR_FROM_SENDER)
- else ->
- Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
+ val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
+ val stateName = chipState?.name ?: "Invalid"
+ logger.logStateChange(stateName, routeInfo.id)
+
+ if (chipState == null) {
+ Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
+ return
+ }
+ uiEventLogger.logReceiverStateChange(chipState)
+
+ if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
+ removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
+ return
+ }
+ if (appIcon == null) {
+ displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ return
}
- }
- override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) {
- setIcon(chipState, currentChipView)
+ appIcon.loadDrawableAsync(
+ context,
+ Icon.OnDrawableLoadedListener { drawable ->
+ displayChip(ChipReceiverInfo(routeInfo, drawable, appName))
+ },
+ // Notify the listener on the main handler since the listener will update
+ // the UI.
+ mainHandler
+ )
}
- private fun stateIntToString(@StatusBarManager.MediaTransferReceiverState state: Int): String {
- return when (state) {
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> CLOSE_TO_SENDER
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> FAR_FROM_SENDER
- else -> "INVALID: $state"
- }
+ override fun updateChipView(chipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
+ setIcon(
+ currentChipView,
+ chipInfo.routeInfo.packageName,
+ chipInfo.appIconDrawableOverride,
+ chipInfo.appNameOverride
+ )
}
}
+data class ChipReceiverInfo(
+ val routeInfo: MediaRoute2Info,
+ val appIconDrawableOverride: Drawable?,
+ val appNameOverride: CharSequence?
+) : ChipInfoCommon {
+ override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
+}
+
private const val RECEIVER_TAG = "MediaTapToTransferRcvr"
-private const val CLOSE_TO_SENDER = "CLOSE_TO_SENDER"
-private const val FAR_FROM_SENDER = "FAR_FROM_SENDER"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt
new file mode 100644
index 000000000000..39a276329a9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLogger.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.receiver
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class for analytics logging for the media tap-to-transfer chip on the receiver device. */
+@SysUISingleton
+class MediaTttReceiverUiEventLogger @Inject constructor(private val logger: UiEventLogger) {
+ /** Logs that the receiver chip has changed states. */
+ fun logReceiverStateChange(chipState: ChipStateReceiver) {
+ logger.log(chipState.uiEvent)
+ }
+}
+
+enum class MediaTttReceiverUiEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs")
+ MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER(982),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_RECEIVER_* docs")
+ MEDIA_TTT_RECEIVER_FAR_FROM_SENDER(983);
+
+ override fun getId() = metricId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 22424a4e9c74..3c6805b4e881 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -16,188 +16,225 @@
package com.android.systemui.media.taptotransfer.sender
+import android.app.StatusBarManager
import android.content.Context
+import android.media.MediaRoute2Info
import android.view.View
+import androidx.annotation.StringRes
+import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
-import com.android.systemui.media.taptotransfer.common.MediaTttChipState
+import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
/**
- * A class that stores all the information necessary to display the media tap-to-transfer chip on
- * the sender device.
+ * A class enumerating all the possible states of the media tap-to-transfer chip on the sender
+ * device.
*
- * This is a sealed class where each subclass represents a specific chip state. Each subclass can
- * contain additional information that is necessary for only that state.
+ * @property stateInt the integer from [StatusBarManager] corresponding with this state.
+ * @property stringResId the res ID of the string that should be displayed in the chip. Null if the
+ * state should not have the chip be displayed.
+ * @property isMidTransfer true if the state represents that a transfer is currently ongoing.
+ * @property isTransferFailure true if the state represents that the transfer has failed.
+ * @property timeout the amount of time this chip should display on the screen before it times out
+ * and disappears.
*/
-sealed class ChipStateSender(
- appPackageName: String?
-) : MediaTttChipState(appPackageName) {
- /** Returns a fully-formed string with the text that the chip should display. */
- abstract fun getChipTextString(context: Context): String
-
- /** Returns true if the loading icon should be displayed and false otherwise. */
- open fun showLoading(): Boolean = false
-
+enum class ChipStateSender(
+ @StatusBarManager.MediaTransferSenderState val stateInt: Int,
+ val uiEvent: UiEventLogger.UiEventEnum,
+ @StringRes val stringResId: Int?,
+ val isMidTransfer: Boolean = false,
+ val isTransferFailure: Boolean = false,
+ val timeout: Long = DEFAULT_TIMEOUT_MILLIS
+) {
/**
- * Returns a click listener for the undo button on the chip. Returns null if this chip state
- * doesn't have an undo button.
- *
- * @param controllerSender passed as a parameter in case we want to display a new chip state
- * when undo is clicked.
+ * A state representing that the two devices are close but not close enough to *start* a cast to
+ * the receiver device. The chip will instruct the user to move closer in order to initiate the
+ * transfer to the receiver.
*/
- open fun undoClickListener(
- controllerSender: MediaTttChipControllerSender
- ): View.OnClickListener? = null
-}
-
-/**
- * A state representing that the two devices are close but not close enough to *start* a cast to
- * the receiver device. The chip will instruct the user to move closer in order to initiate the
- * transfer to the receiver.
- *
- * @property otherDeviceName the name of the other device involved in the transfer.
- */
-class AlmostCloseToStartCast(
- appPackageName: String?,
- private val otherDeviceName: String,
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_move_closer_to_start_cast, otherDeviceName)
- }
-}
-
-/**
- * A state representing that the two devices are close but not close enough to *end* a cast that's
- * currently occurring the receiver device. The chip will instruct the user to move closer in order
- * to initiate the transfer from the receiver and back onto this device (the original sender).
- *
- * @property otherDeviceName the name of the other device involved in the transfer.
- */
-class AlmostCloseToEndCast(
- appPackageName: String?,
- private val otherDeviceName: String,
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_move_closer_to_end_cast, otherDeviceName)
- }
-}
-
-/**
- * A state representing that a transfer to the receiver device has been initiated (but not
- * completed).
- *
- * @property otherDeviceName the name of the other device involved in the transfer.
- */
-class TransferToReceiverTriggered(
- appPackageName: String?,
- private val otherDeviceName: String
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
- }
-
- override fun showLoading() = true
- override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
-}
+ ALMOST_CLOSE_TO_START_CAST(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST,
+ R.string.media_move_closer_to_start_cast,
+ ),
-/**
- * A state representing that a transfer from the receiver device and back to this device (the
- * sender) has been initiated (but not completed).
- */
-class TransferToThisDeviceTriggered(
- appPackageName: String?,
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing_this_device)
- }
+ /**
+ * A state representing that the two devices are close but not close enough to *end* a cast
+ * that's currently occurring the receiver device. The chip will instruct the user to move
+ * closer in order to initiate the transfer from the receiver and back onto this device (the
+ * original sender).
+ */
+ ALMOST_CLOSE_TO_END_CAST(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST,
+ R.string.media_move_closer_to_end_cast,
+ ),
- override fun showLoading() = true
- override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
-}
+ /**
+ * A state representing that a transfer to the receiver device has been initiated (but not
+ * completed).
+ */
+ TRANSFER_TO_RECEIVER_TRIGGERED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED,
+ R.string.media_transfer_playing_different_device,
+ isMidTransfer = true,
+ timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
+ ),
-/**
- * A state representing that a transfer to the receiver device has been successfully completed.
- *
- * @property otherDeviceName the name of the other device involved in the transfer.
- * @property undoCallback if present, the callback that should be called when the user clicks the
- * undo button. The undo button will only be shown if this is non-null.
- */
-class TransferToReceiverSucceeded(
- appPackageName: String?,
- private val otherDeviceName: String,
- val undoCallback: IUndoMediaTransferCallback? = null
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
- }
+ /**
+ * A state representing that a transfer from the receiver device and back to this device (the
+ * sender) has been initiated (but not completed).
+ */
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ R.string.media_transfer_playing_this_device,
+ isMidTransfer = true,
+ timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
+ ),
- override fun undoClickListener(
- controllerSender: MediaTttChipControllerSender
- ): View.OnClickListener? {
- if (undoCallback == null) {
- return null
+ /**
+ * A state representing that a transfer to the receiver device has been successfully completed.
+ */
+ TRANSFER_TO_RECEIVER_SUCCEEDED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ R.string.media_transfer_playing_different_device
+ ) {
+ override fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback?,
+ uiEventLogger: MediaTttSenderUiEventLogger
+ ): View.OnClickListener? {
+ if (undoCallback == null) {
+ return null
+ }
+ return View.OnClickListener {
+ uiEventLogger.logUndoClicked(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
+ )
+ undoCallback.onUndoTriggered()
+ // The external service should eventually send us a TransferToThisDeviceTriggered
+ // state, but that may take too long to go through the binder and the user may be
+ // confused ast o why the UI hasn't changed yet. So, we immediately change the UI
+ // here.
+ controllerSender.displayChip(
+ ChipSenderInfo(
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
+ )
+ )
+ }
}
+ },
- return View.OnClickListener {
- this.undoCallback.onUndoTriggered()
- // The external service should eventually send us a TransferToThisDeviceTriggered state,
- // but that may take too long to go through the binder and the user may be confused as
- // to why the UI hasn't changed yet. So, we immediately change the UI here.
- controllerSender.displayChip(
- TransferToThisDeviceTriggered(this.appPackageName)
- )
+ /**
+ * A state representing that a transfer back to this device has been successfully completed.
+ */
+ TRANSFER_TO_THIS_DEVICE_SUCCEEDED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ R.string.media_transfer_playing_this_device
+ ) {
+ override fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback?,
+ uiEventLogger: MediaTttSenderUiEventLogger
+ ): View.OnClickListener? {
+ if (undoCallback == null) {
+ return null
+ }
+ return View.OnClickListener {
+ uiEventLogger.logUndoClicked(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
+ )
+ undoCallback.onUndoTriggered()
+ // The external service should eventually send us a TransferToReceiverTriggered
+ // state, but that may take too long to go through the binder and the user may be
+ // confused as to why the UI hasn't changed yet. So, we immediately change the UI
+ // here.
+ controllerSender.displayChip(
+ ChipSenderInfo(
+ TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
+ )
+ )
+ }
}
- }
-}
-
-/**
- * A state representing that a transfer back to this device has been successfully completed.
- *
- * @property otherDeviceName the name of the other device involved in the transfer.
- * @property undoCallback if present, the callback that should be called when the user clicks the
- * undo button. The undo button will only be shown if this is non-null.
- */
-class TransferToThisDeviceSucceeded(
- appPackageName: String?,
- private val otherDeviceName: String,
- val undoCallback: IUndoMediaTransferCallback? = null
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing_this_device)
- }
+ },
+
+ /** A state representing that a transfer to the receiver device has failed. */
+ TRANSFER_TO_RECEIVER_FAILED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED,
+ R.string.media_transfer_failed,
+ isTransferFailure = true
+ ),
+
+ /** A state representing that a transfer back to this device has failed. */
+ TRANSFER_TO_THIS_DEVICE_FAILED(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED,
+ R.string.media_transfer_failed,
+ isTransferFailure = true
+ ),
+
+ /** A state representing that this device is far away from any receiver device. */
+ FAR_FROM_RECEIVER(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER,
+ stringResId = null
+ );
- override fun undoClickListener(
- controllerSender: MediaTttChipControllerSender
- ): View.OnClickListener? {
- if (undoCallback == null) {
+ /**
+ * Returns a fully-formed string with the text that the chip should display.
+ *
+ * @param otherDeviceName the name of the other device involved in the transfer.
+ */
+ fun getChipTextString(context: Context, otherDeviceName: String): String? {
+ if (stringResId == null) {
return null
}
-
- return View.OnClickListener {
- this.undoCallback.onUndoTriggered()
- // The external service should eventually send us a TransferToReceiverTriggered state,
- // but that may take too long to go through the binder and the user may be confused as
- // to why the UI hasn't changed yet. So, we immediately change the UI here.
- controllerSender.displayChip(
- TransferToReceiverTriggered(
- this.appPackageName,
- this.otherDeviceName
- )
- )
- }
+ return context.getString(stringResId, otherDeviceName)
}
-}
-/** A state representing that a transfer has failed. */
-class TransferFailed(
- appPackageName: String?,
-) : ChipStateSender(appPackageName) {
- override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_failed)
+ /**
+ * Returns a click listener for the undo button on the chip. Returns null if this chip state
+ * doesn't have an undo button.
+ *
+ * @param controllerSender passed as a parameter in case we want to display a new chip state
+ * when undo is clicked.
+ * @param undoCallback if present, the callback that should be called when the user clicks the
+ * undo button. The undo button will only be shown if this is non-null.
+ */
+ open fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback?,
+ uiEventLogger: MediaTttSenderUiEventLogger
+ ): View.OnClickListener? = null
+
+ companion object {
+ /**
+ * Returns the sender state enum associated with the given [displayState] from
+ * [StatusBarManager].
+ */
+ fun getSenderStateFromId(
+ @StatusBarManager.MediaTransferSenderState displayState: Int,
+ ): ChipStateSender = values().first { it.stateInt == displayState }
+
+ /**
+ * Returns the state int from [StatusBarManager] associated with the given sender state
+ * name.
+ *
+ * @param name the name of one of the [ChipStateSender] enums.
+ */
+ @StatusBarManager.MediaTransferSenderState
+ fun getSenderStateIdFromName(name: String): Int = valueOf(name).stateInt
}
}
// Give the Transfer*Triggered states a longer timeout since those states represent an active
// process and we should keep the user informed about it as long as possible (but don't allow it to
// continue indefinitely).
-private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L \ No newline at end of file
+private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index da2aac4d5ad7..9f5ec7e1a330 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager
import android.content.Context
import android.media.MediaRoute2Info
+import android.os.PowerManager
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -28,6 +29,7 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
@@ -50,13 +52,16 @@ class MediaTttChipControllerSender @Inject constructor(
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
-) : MediaTttChipControllerCommon<ChipStateSender>(
+ powerManager: PowerManager,
+ private val uiEventLogger: MediaTttSenderUiEventLogger
+) : MediaTttChipControllerCommon<ChipSenderInfo>(
context,
logger,
windowManager,
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip
) {
private var currentlyDisplayedChipState: ChipStateSender? = null
@@ -82,104 +87,83 @@ class MediaTttChipControllerSender @Inject constructor(
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?
) {
- logger.logStateChange(stateIntToString(displayState), routeInfo.id)
- val appPackageName = routeInfo.packageName
- val otherDeviceName = routeInfo.name.toString()
- val chipState = when(displayState) {
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
- AlmostCloseToStartCast(appPackageName, otherDeviceName)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
- AlmostCloseToEndCast(appPackageName, otherDeviceName)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
- TransferToReceiverTriggered(appPackageName, otherDeviceName)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
- TransferToThisDeviceTriggered(appPackageName)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
- TransferToReceiverSucceeded(appPackageName, otherDeviceName, undoCallback)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
- TransferToThisDeviceSucceeded(appPackageName, otherDeviceName, undoCallback)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
- TransferFailed(appPackageName)
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER -> {
- removeChip(removalReason = FAR_FROM_RECEIVER)
- null
- }
- else -> {
- Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
- null
- }
+ val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
+ val stateName = chipState?.name ?: "Invalid"
+ logger.logStateChange(stateName, routeInfo.id)
+
+ if (chipState == null) {
+ Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
+ return
}
+ uiEventLogger.logSenderStateChange(chipState)
- chipState?.let {
- displayChip(it)
+ if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
+ removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
+ } else {
+ displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
}
/** Displays the chip view for the given state. */
- override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) {
+ override fun updateChipView(
+ chipInfo: ChipSenderInfo,
+ currentChipView: ViewGroup) {
+ val chipState = chipInfo.state
currentlyDisplayedChipState = chipState
// App icon
- setIcon(chipState, currentChipView)
+ setIcon(currentChipView, chipInfo.routeInfo.packageName)
// Text
+ val otherDeviceName = chipInfo.routeInfo.name.toString()
currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = chipState.getChipTextString(context)
+ text = chipState.getChipTextString(context, otherDeviceName)
}
// Loading
currentChipView.requireViewById<View>(R.id.loading).visibility =
- if (chipState.showLoading()) { View.VISIBLE } else { View.GONE }
+ chipState.isMidTransfer.visibleIfTrue()
+
// Undo
val undoView = currentChipView.requireViewById<View>(R.id.undo)
- val undoClickListener = chipState.undoClickListener(this)
+ val undoClickListener = chipState.undoClickListener(
+ this, chipInfo.routeInfo, chipInfo.undoCallback, uiEventLogger
+ )
undoView.setOnClickListener(undoClickListener)
- undoView.visibility = if (undoClickListener != null) { View.VISIBLE } else { View.GONE }
+ undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure
- val showFailure = chipState is TransferFailed
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
- if (showFailure) { View.VISIBLE } else { View.GONE }
+ chipState.isTransferFailure.visibleIfTrue()
}
override fun removeChip(removalReason: String) {
// Don't remove the chip if we're mid-transfer since the user should still be able to
// see the status of the transfer. (But do remove it if it's finally timed out.)
- if ((currentlyDisplayedChipState is TransferToReceiverTriggered ||
- currentlyDisplayedChipState is TransferToThisDeviceTriggered)
- && removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
+ if (currentlyDisplayedChipState?.isMidTransfer == true
+ && removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
return
}
super.removeChip(removalReason)
currentlyDisplayedChipState = null
}
- private fun stateIntToString(@StatusBarManager.MediaTransferSenderState state: Int): String {
- return when(state) {
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
- "ALMOST_CLOSE_TO_START_CAST"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
- "ALMOST_CLOSE_TO_END_CAST"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
- "TRANSFER_TO_RECEIVER_TRIGGERED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
- "TRANSFER_TO_THIS_DEVICE_TRIGGERED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
- "TRANSFER_TO_RECEIVER_SUCCEEDED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
- "TRANSFER_TO_THIS_DEVICE_SUCCEEDED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED ->
- "TRANSFER_TO_RECEIVER_FAILED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
- "TRANSFER_TO_THIS_DEVICE_FAILED"
- StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER ->
- FAR_FROM_RECEIVER
- else -> "INVALID: $state"
+ private fun Boolean.visibleIfTrue(): Int {
+ return if (this) {
+ View.VISIBLE
+ } else {
+ View.GONE
}
}
}
+data class ChipSenderInfo(
+ val state: ChipStateSender,
+ val routeInfo: MediaRoute2Info,
+ val undoCallback: IUndoMediaTransferCallback? = null
+) : ChipInfoCommon {
+ override fun getTimeoutMs() = state.timeout
+}
+
const val SENDER_TAG = "MediaTapToTransferSender"
-private const val FAR_FROM_RECEIVER = "FAR_FROM_RECEIVER"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
new file mode 100644
index 000000000000..af3c1b60bacf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
@@ -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 com.android.systemui.media.taptotransfer.sender
+
+import android.util.Log
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class for analytics logging for the media tap-to-transfer chip on the sender device. */
+@SysUISingleton
+class MediaTttSenderUiEventLogger @Inject constructor(private val logger: UiEventLogger) {
+ /** Logs that the sender chip has changed states. */
+ fun logSenderStateChange(chipState: ChipStateSender) {
+ logger.log(chipState.uiEvent)
+ }
+
+ /**
+ * Logs that the undo button was clicked.
+ *
+ * @param undoUiEvent the uiEvent specific to which undo button was clicked.
+ */
+ fun logUndoClicked(undoUiEvent: UiEventLogger.UiEventEnum) {
+ val isUndoEvent =
+ undoUiEvent == MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
+ || undoUiEvent ==
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
+ if (!isUndoEvent) {
+ Log.w(
+ MediaTttSenderUiEventLogger::class.simpleName!!,
+ "Must pass an undo-specific UiEvent."
+ )
+ return
+ }
+ logger.log(undoUiEvent)
+ }
+}
+
+enum class MediaTttSenderUiEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The undo button on the media ttt chip on the sender device was clicked " +
+ "to undo the transfer to the receiver device")
+ MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED(971),
+ @UiEvent(doc = "The undo button on the media ttt chip on the sender device was clicked " +
+ "to undo the transfer back to this device")
+ MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED(972),
+
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST(973),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST(974),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED(975),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED(976),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED(977),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED(978),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED(979),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED(980),
+ @UiEvent(doc = "See android.app.StatusBarManager.MEDIA_TRANSFER_SENDER_* docs")
+ MEDIA_TTT_SENDER_FAR_FROM_RECEIVER(981);
+
+ override fun getId() = metricId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index e19483ae7845..b4e20fd7f32b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -97,7 +97,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
"SHOWING_AUTO_SAVER_SUGGESTION",
};
- private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
+ private static final String ACTION_SHOW_BATTERY_SAVER_SETTINGS = "PNW.batterySaverSettings";
private static final String ACTION_START_SAVER = "PNW.startSaver";
private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
private static final String ACTION_CLICKED_TEMP_WARNING = "PNW.clickedTempWarning";
@@ -138,6 +138,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Receiver mReceiver = new Receiver();
private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
+ private final Intent mOpenBatterySaverSettings =
+ settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
private final boolean mUseSevereDialog;
private int mBatteryLevel;
@@ -287,7 +289,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
- nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
+ nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SAVER_SETTINGS));
}
// Make the notification red if the percentage goes below a certain amount or the time
// remaining estimate is disabled
@@ -748,7 +750,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
public void init() {
IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
+ filter.addAction(ACTION_SHOW_BATTERY_SAVER_SETTINGS);
filter.addAction(ACTION_START_SAVER);
filter.addAction(ACTION_DISMISSED_WARNING);
filter.addAction(ACTION_CLICKED_TEMP_WARNING);
@@ -768,9 +770,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Slog.i(TAG, "Received " + action);
- if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) {
+ if (action.equals(ACTION_SHOW_BATTERY_SAVER_SETTINGS)) {
dismissLowBatteryNotification();
- mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT);
+ mContext.startActivityAsUser(mOpenBatterySaverSettings, UserHandle.CURRENT);
} else if (action.equals(ACTION_START_SAVER)) {
setSaverMode(true, true);
dismissLowBatteryNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5d9361d201c1..e0d158cd7db6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -18,7 +18,10 @@ package com.android.systemui.qs
import android.app.IActivityManager
import android.app.IForegroundServiceObserver
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.IBinder
@@ -42,6 +45,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -67,6 +71,7 @@ class FgsManagerController @Inject constructor(
private val packageManager: PackageManager,
private val deviceConfigProxy: DeviceConfigProxy,
private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val broadcastDispatcher: BroadcastDispatcher,
private val dumpManager: DumpManager
) : IForegroundServiceObserver.Stub(), Dumpable {
@@ -125,6 +130,18 @@ class FgsManagerController @Inject constructor(
dumpManager.registerDumpable(this)
+ broadcastDispatcher.registerReceiver(
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER) {
+ showDialog(null)
+ }
+ }
+ },
+ IntentFilter(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER),
+ executor = mainExecutor,
+ flags = Context.RECEIVER_NOT_EXPORTED)
+
initialized = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 00142799c541..865f09337fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -199,9 +199,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
/** */
public void setListening(boolean listening, boolean expanded) {
- // TODO(218268829): checking for split shade is workaround but when proper fix lands
- // "|| mShouldUseSplitNotificationShade" should be removed
- setListening(listening && (expanded || mShouldUseSplitNotificationShade));
+ setListening(listening && expanded);
if (mView.isListening()) {
refreshAllTiles();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
new file mode 100644
index 000000000000..0b987677eac9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.systemui.R;
+
+/**
+ * ConstraintLayout that is draggable when touched in a specific region
+ */
+public class DraggableConstraintLayout extends ConstraintLayout
+ implements ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ private final SwipeDismissHandler mSwipeDismissHandler;
+ private final GestureDetector mSwipeDetector;
+ private View mActionsContainer;
+ private SwipeDismissCallbacks mCallbacks;
+
+ /**
+ * Stores the callbacks when the view is interacted with or dismissed.
+ */
+ public interface SwipeDismissCallbacks {
+ /**
+ * Run when the view is interacted with (touched)
+ */
+ default void onInteraction() {
+
+ }
+
+ /**
+ * Run when the view is dismissed (the distance threshold is met), pre-dismissal animation
+ */
+ default void onSwipeDismissInitiated(Animator animator) {
+
+ }
+
+ /**
+ * Run when the view is dismissed (the distance threshold is met), post-dismissal animation
+ */
+ default void onDismissComplete() {
+
+ }
+ }
+
+ public DraggableConstraintLayout(Context context) {
+ this(context, null);
+ }
+
+ public DraggableConstraintLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DraggableConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mSwipeDismissHandler = new SwipeDismissHandler(mContext, this);
+ setOnTouchListener(mSwipeDismissHandler);
+
+ mSwipeDetector = new GestureDetector(mContext,
+ new GestureDetector.SimpleOnGestureListener() {
+ final Rect mActionsRect = new Rect();
+
+ @Override
+ public boolean onScroll(
+ MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+ mActionsContainer.getBoundsOnScreen(mActionsRect);
+ // return true if we aren't in the actions bar, or if we are but it isn't
+ // scrollable in the direction of movement
+ return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
+ || !mActionsContainer.canScrollHorizontally((int) distanceX);
+ }
+ });
+ mSwipeDetector.setIsLongpressEnabled(false);
+ }
+
+ public void setCallbacks(SwipeDismissCallbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override // View
+ protected void onFinishInflate() {
+ mActionsContainer = findViewById(R.id.actions_container_background);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mSwipeDismissHandler.onTouch(this, ev);
+ }
+ return mSwipeDetector.onTouchEvent(ev);
+ }
+
+ public int getVisibleRight() {
+ return mActionsContainer.getRight();
+ }
+
+ /**
+ * Cancel current dismissal animation, if any
+ */
+ public void cancelDismissal() {
+ mSwipeDismissHandler.cancel();
+ }
+
+ /**
+ * Return whether the view is currently dismissing
+ */
+ public boolean isDismissing() {
+ return mSwipeDismissHandler.isDismissing();
+ }
+
+ /**
+ * Dismiss the view, with animation controlled by SwipeDismissHandler
+ */
+ public void dismiss() {
+ mSwipeDismissHandler.dismiss();
+ }
+
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ // Only child views are touchable.
+ Region r = new Region();
+ Rect rect = new Rect();
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).getGlobalVisibleRect(rect);
+ r.op(rect, Region.Op.UNION);
+ }
+ inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ inoutInfo.touchableRegion.set(r);
+ }
+
+ /**
+ * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not
+ * met
+ */
+ private class SwipeDismissHandler implements OnTouchListener {
+ private static final String TAG = "SwipeDismissHandler";
+
+ // distance needed to register a dismissal
+ private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
+
+ private final DraggableConstraintLayout mView;
+ private final GestureDetector mGestureDetector;
+ private final DisplayMetrics mDisplayMetrics;
+ private ValueAnimator mDismissAnimation;
+
+ private float mStartX;
+ // Keeps track of the most recent direction (between the last two move events).
+ // -1 for left; +1 for right.
+ private int mDirectionX;
+ private float mPreviousX;
+
+ SwipeDismissHandler(Context context, DraggableConstraintLayout view) {
+ mView = view;
+ GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
+ mGestureDetector = new GestureDetector(context, gestureListener);
+ mDisplayMetrics = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(mDisplayMetrics);
+ mCallbacks = new SwipeDismissCallbacks() {
+ }; // default to unimplemented callbacks
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ boolean gestureResult = mGestureDetector.onTouchEvent(event);
+ mCallbacks.onInteraction();
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mStartX = event.getRawX();
+ mPreviousX = mStartX;
+ return true;
+ } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+ return true;
+ }
+ if (isPastDismissThreshold()) {
+ dismiss();
+ } else {
+ // if we've moved, but not past the threshold, start the return animation
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "swipe gesture abandoned");
+ }
+ createSwipeReturnAnimation().start();
+ }
+ return true;
+ }
+ return gestureResult;
+ }
+
+ class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onScroll(
+ MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+ mView.setTranslationX(ev2.getRawX() - mStartX);
+ mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
+ mPreviousX = ev2.getRawX();
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ if (mView.getTranslationX() * velocityX > 0
+ && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
+ ValueAnimator dismissAnimator =
+ createSwipeDismissAnimation(velocityX / (float) 1000);
+ mCallbacks.onSwipeDismissInitiated(dismissAnimator);
+ dismiss(dismissAnimator);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private boolean isPastDismissThreshold() {
+ float translationX = mView.getTranslationX();
+ // Determines whether the absolute translation from the start is in the same direction
+ // as the current movement. For example, if the user moves most of the way to the right,
+ // but then starts dragging back left, we do not dismiss even though the absolute
+ // distance is greater than the threshold.
+ if (translationX * mDirectionX > 0) {
+ return Math.abs(translationX) >= FloatingWindowUtil.dpToPx(mDisplayMetrics,
+ DISMISS_DISTANCE_THRESHOLD_DP);
+ }
+ return false;
+ }
+
+ boolean isDismissing() {
+ return (mDismissAnimation != null && mDismissAnimation.isRunning());
+ }
+
+ void cancel() {
+ if (isDismissing()) {
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "cancelling dismiss animation");
+ }
+ mDismissAnimation.cancel();
+ }
+ }
+
+ void dismiss() {
+ ValueAnimator anim = createSwipeDismissAnimation(3);
+ mCallbacks.onSwipeDismissInitiated(anim);
+ dismiss(anim);
+ }
+
+ private void dismiss(ValueAnimator animator) {
+ mDismissAnimation = animator;
+ mDismissAnimation.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);
+ if (!mCancelled) {
+ mCallbacks.onDismissComplete();
+ }
+ }
+ });
+ mDismissAnimation.start();
+ }
+
+ private ValueAnimator createSwipeDismissAnimation(float velocity) {
+ // velocity is measured in pixels per millisecond
+ velocity = Math.min(3, Math.max(1, velocity));
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ float startX = mView.getTranslationX();
+ // make sure the UI gets all the way off the screen in the direction of movement
+ // (the actions container background is guaranteed to be both the leftmost and
+ // rightmost UI element in LTR and RTL)
+ float finalX;
+ int layoutDir =
+ mView.getContext().getResources().getConfiguration().getLayoutDirection();
+ if (startX > 0 || (startX == 0 && layoutDir == LAYOUT_DIRECTION_RTL)) {
+ finalX = mDisplayMetrics.widthPixels;
+ } else {
+ finalX = -1 * mActionsContainer.getRight();
+ }
+ float distance = Math.abs(finalX - startX);
+
+ anim.addUpdateListener(animation -> {
+ float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
+ mView.setTranslationX(translation);
+ mView.setAlpha(1 - animation.getAnimatedFraction());
+ });
+ anim.setDuration((long) (distance / Math.abs(velocity)));
+ return anim;
+ }
+
+ private ValueAnimator createSwipeReturnAnimation() {
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ float startX = mView.getTranslationX();
+ float finalX = 0;
+
+ anim.addUpdateListener(animation -> {
+ float translation = MathUtils.lerp(
+ startX, finalX, animation.getAnimatedFraction());
+ mView.setTranslationX(translation);
+ });
+
+ return anim;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 6d729b92e7b8..6af6e36a75f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -141,7 +141,7 @@ public class ScreenshotView extends FrameLayout implements
private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mScrollingScrim;
- private View mScreenshotStatic;
+ private DraggableConstraintLayout mScreenshotStatic;
private ImageView mScreenshotPreview;
private View mScreenshotPreviewBorder;
private ImageView mScrollablePreview;
@@ -159,7 +159,6 @@ public class ScreenshotView extends FrameLayout implements
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
private boolean mPendingSharedTransition;
- private SwipeDismissHandler mSwipeDismissHandler;
private InputMonitorCompat mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private boolean mShowScrollablePreview;
@@ -332,19 +331,6 @@ public class ScreenshotView extends FrameLayout implements
}
}
- @Override // ViewGroup
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // scrolling scrim should not be swipeable; return early if we're on the scrim
- if (!getSwipeRegion().contains((int) ev.getRawX(), (int) ev.getRawY())) {
- return false;
- }
- // always pass through the down event so the swipe handler knows the initial state
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mSwipeDismissHandler.onTouch(this, ev);
- }
- return mSwipeDetector.onTouchEvent(ev);
- }
-
@Override // View
protected void onFinishInflate() {
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
@@ -356,8 +342,8 @@ public class ScreenshotView extends FrameLayout implements
mScreenshotPreview.setClipToOutline(true);
mActionsContainerBackground = requireNonNull(findViewById(
- R.id.screenshot_actions_container_background));
- mActionsContainer = requireNonNull(findViewById(R.id.screenshot_actions_container));
+ R.id.actions_container_background));
+ mActionsContainer = requireNonNull(findViewById(R.id.actions_container));
mActionsView = requireNonNull(findViewById(R.id.screenshot_actions));
mBackgroundProtection = requireNonNull(
findViewById(R.id.screenshot_actions_background));
@@ -395,35 +381,34 @@ public class ScreenshotView extends FrameLayout implements
setFocusableInTouchMode(true);
requestFocus();
- mSwipeDismissHandler = new SwipeDismissHandler(mContext, mScreenshotStatic,
- new SwipeDismissHandler.SwipeDismissCallbacks() {
- @Override
- public void onInteraction() {
- mCallbacks.onUserInteraction();
- }
-
- @Override
- public void onSwipeDismissInitiated(Animator anim) {
- if (DEBUG_DISMISS) {
- Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture");
- }
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
- mPackageName);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mBackgroundProtection.animate()
- .alpha(0).setDuration(anim.getDuration()).start();
- }
- });
- }
+ mScreenshotStatic.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
+ @Override
+ public void onInteraction() {
+ mCallbacks.onUserInteraction();
+ }
+ @Override
+ public void onSwipeDismissInitiated(Animator animator) {
+ if (DEBUG_DISMISS) {
+ Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture");
+ }
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
+ mPackageName);
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onDismissComplete() {
- mCallbacks.onDismiss();
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mBackgroundProtection.animate()
+ .alpha(0).setDuration(animation.getDuration()).start();
}
});
+ }
+
+ @Override
+ public void onDismissComplete() {
+ mCallbacks.onDismiss();
+ }
+ });
}
View getScreenshotPreview() {
@@ -648,8 +633,6 @@ public class ScreenshotView extends FrameLayout implements
requestLayout();
createScreenshotActionsShadeAnimation().start();
-
- setOnTouchListener(mSwipeDismissHandler);
}
});
@@ -958,7 +941,7 @@ public class ScreenshotView extends FrameLayout implements
}
boolean isDismissing() {
- return mSwipeDismissHandler.isDismissing();
+ return mScreenshotStatic.isDismissing();
}
boolean isPendingSharedTransition() {
@@ -966,15 +949,14 @@ public class ScreenshotView extends FrameLayout implements
}
void animateDismissal() {
- mSwipeDismissHandler.dismiss();
+ mScreenshotStatic.dismiss();
}
void reset() {
if (DEBUG_UI) {
Log.d(TAG, "reset screenshot view");
}
-
- mSwipeDismissHandler.cancel();
+ mScreenshotStatic.cancelDismissal();
if (DEBUG_WINDOW) {
Log.d(TAG, "removing OnComputeInternalInsetsListener");
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
deleted file mode 100644
index 24b1249c3f98..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not met
- */
-public class SwipeDismissHandler implements View.OnTouchListener {
- private static final String TAG = "SwipeDismissHandler";
-
- // distance needed to register a dismissal
- private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
-
- /**
- * Stores the callbacks when the view is interacted with or dismissed.
- */
- public interface SwipeDismissCallbacks {
- /**
- * Run when the view is interacted with (touched)
- */
- void onInteraction();
-
- /**
- * Run when the view is dismissed (the distance threshold is met), pre-dismissal animation
- */
- void onSwipeDismissInitiated(Animator animator);
-
- /**
- * Run when the view is dismissed (the distance threshold is met), post-dismissal animation
- */
- void onDismissComplete();
- }
-
- private final View mView;
- private final SwipeDismissCallbacks mCallbacks;
- private final GestureDetector mGestureDetector;
- private DisplayMetrics mDisplayMetrics;
- private ValueAnimator mDismissAnimation;
-
-
- private float mStartX;
- // Keeps track of the most recent direction (between the last two move events).
- // -1 for left; +1 for right.
- private int mDirectionX;
- private float mPreviousX;
-
- public SwipeDismissHandler(Context context, View view, SwipeDismissCallbacks callbacks) {
- mView = view;
- mCallbacks = callbacks;
- GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
- mGestureDetector = new GestureDetector(context, gestureListener);
- mDisplayMetrics = new DisplayMetrics();
- context.getDisplay().getRealMetrics(mDisplayMetrics);
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- boolean gestureResult = mGestureDetector.onTouchEvent(event);
- mCallbacks.onInteraction();
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mStartX = event.getRawX();
- mPreviousX = mStartX;
- return true;
- } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
- return true;
- }
- if (isPastDismissThreshold()) {
- ValueAnimator dismissAnimator = createSwipeDismissAnimation(1);
- mCallbacks.onSwipeDismissInitiated(dismissAnimator);
- dismiss(dismissAnimator);
- } else {
- // if we've moved, but not past the threshold, start the return animation
- if (DEBUG_DISMISS) {
- Log.d(TAG, "swipe gesture abandoned");
- }
- createSwipeReturnAnimation().start();
- }
- return true;
- }
- return gestureResult;
- }
-
- class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onScroll(
- MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
- mView.setTranslationX(ev2.getRawX() - mStartX);
- mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
- mPreviousX = ev2.getRawX();
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- if (mView.getTranslationX() * velocityX > 0
- && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
- ValueAnimator dismissAnimator =
- createSwipeDismissAnimation(velocityX / (float) 1000);
- mCallbacks.onSwipeDismissInitiated(dismissAnimator);
- dismiss(dismissAnimator);
- return true;
- }
- return false;
- }
- }
-
- private boolean isPastDismissThreshold() {
- float translationX = mView.getTranslationX();
- // Determines whether the absolute translation from the start is in the same direction
- // as the current movement. For example, if the user moves most of the way to the right,
- // but then starts dragging back left, we do not dismiss even though the absolute
- // distance is greater than the threshold.
- if (translationX * mDirectionX > 0) {
- return Math.abs(translationX) >= FloatingWindowUtil.dpToPx(mDisplayMetrics,
- DISMISS_DISTANCE_THRESHOLD_DP);
- }
- return false;
- }
-
- /**
- * Return whether the view is currently being dismissed
- */
- public boolean isDismissing() {
- return (mDismissAnimation != null && mDismissAnimation.isRunning());
- }
-
- /**
- * Cancel the currently-running dismissal animation, if any.
- */
- public void cancel() {
- if (isDismissing()) {
- if (DEBUG_ANIM) {
- Log.d(TAG, "cancelling dismiss animation");
- }
- mDismissAnimation.cancel();
- }
- }
-
- /**
- * Start dismissal animation (will run onDismiss callback when animation complete)
- */
- public void dismiss() {
- dismiss(createSwipeDismissAnimation(1));
- }
-
- private void dismiss(ValueAnimator animator) {
- mDismissAnimation = animator;
- mDismissAnimation.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);
- if (!mCancelled) {
- mCallbacks.onDismissComplete();
- }
- }
- });
- mDismissAnimation.start();
- }
-
- private ValueAnimator createSwipeDismissAnimation(float velocity) {
- // velocity is measured in pixels per millisecond
- velocity = Math.min(3, Math.max(1, velocity));
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mView.getTranslationX();
- // make sure the UI gets all the way off the screen in the direction of movement
- // (the actions container background is guaranteed to be both the leftmost and
- // rightmost UI element in LTR and RTL)
- float finalX;
- int layoutDir = mView.getContext().getResources().getConfiguration().getLayoutDirection();
- if (startX > 0 || (startX == 0 && layoutDir == View.LAYOUT_DIRECTION_RTL)) {
- finalX = mDisplayMetrics.widthPixels;
- } else {
- finalX = -1 * mView.getRight();
- }
- float distance = Math.abs(finalX - startX);
-
- anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
- mView.setTranslationX(translation);
- mView.setAlpha(1 - animation.getAnimatedFraction());
- });
- anim.setDuration((long) (distance / Math.abs(velocity)));
- return anim;
- }
-
- private ValueAnimator createSwipeReturnAnimation() {
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mView.getTranslationX();
- float finalX = 0;
-
- anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(
- startX, finalX, animation.getAnimatedFraction());
- mView.setTranslationX(translation);
- });
-
- return anim;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 98e6bd141b65..924351df3117 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -51,7 +51,6 @@ import androidx.annotation.NonNull;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
-import com.android.systemui.shared.recents.utilities.BitmapUtil;
import java.util.function.Consumer;
@@ -208,7 +207,7 @@ public class TakeScreenshotService extends Service {
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
}
- Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
+ Bitmap screenshot = ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap(
screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
Insets insets = screenshotRequest.getInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt b/packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt
new file mode 100644
index 000000000000..aa2bcecbd224
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.smartspace
+
+/**
+ * A {@link SmartspacePrecondition} captures the conditions that must be met for Smartspace to be
+ * used in a particular setting.
+ */
+interface SmartspacePrecondition {
+ /**
+ * A callback for receiving updates when conditions have changed.
+ */
+ interface Listener {
+ fun onCriteriaChanged()
+ }
+
+ /**
+ * Adds a listener to receive future updates. {@link Listener#onCriteriaChanged} will be called
+ * immediately upon adding.
+ */
+ fun addListener(listener: Listener)
+
+ /**
+ * Removes a listener from receiving future updates.
+ */
+ fun removeListener(listener: Listener)
+
+ /**
+ * Returns whether all conditions have been met.
+ */
+ fun conditionsMet(): Boolean
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt
new file mode 100644
index 000000000000..7228550e4e41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.smartspace
+
+import android.app.smartspace.SmartspaceTarget
+
+/**
+ * {@link SmartspaceTargetFilter} defines a way to locally filter targets from inclusion. This
+ * should be used for filtering that isn't available further upstream.
+ */
+interface SmartspaceTargetFilter {
+ /**
+ * An interface implemented by clients to receive updates when the filtering criteria changes.
+ * When this happens, the client should refresh their target set.
+ */
+ interface Listener {
+ fun onCriteriaChanged()
+ }
+
+ /**
+ * Adds a listener to receive future updates. {@link Listener#onCriteriaChanged} will be
+ * invoked immediately after.
+ */
+ fun addListener(listener: Listener)
+
+ /**
+ * Removes listener from receiving future updates.
+ */
+ fun removeListener(listener: Listener)
+
+ /**
+ * Returns {@code true} if the {@link SmartspaceTarget} should be included in the current
+ * target set, {@code false} otherwise.
+ */
+ fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
new file mode 100644
index 000000000000..1b74ac36ebf0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -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 com.android.systemui.smartspace.dagger
+
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.smartspace.SmartspacePrecondition
+import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
+import dagger.Binds
+import dagger.BindsOptionalOf
+import dagger.Module
+import javax.inject.Named
+
+@Module(subcomponents = [SmartspaceViewComponent::class])
+abstract class SmartspaceModule {
+ @Module
+ companion object {
+ /**
+ * The BcSmartspaceDataProvider for dreams.
+ */
+ const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin"
+
+ /**
+ * The lockscreen smartspace target filter.
+ */
+ const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter"
+
+ /**
+ * The dream smartspace target filter.
+ */
+ const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter"
+
+ /**
+ * The precondition for dream smartspace
+ */
+ const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"
+ }
+
+ @BindsOptionalOf
+ @Named(DREAM_SMARTSPACE_TARGET_FILTER)
+ abstract fun optionalDreamSmartspaceTargetFilter(): SmartspaceTargetFilter?
+
+ @BindsOptionalOf
+ @Named(DREAM_SMARTSPACE_DATA_PLUGIN)
+ abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin?
+
+ @Binds
+ @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
+ abstract fun provideLockscreenSmartspaceTargetFilter(
+ filter: LockscreenTargetFilter?
+ ): SmartspaceTargetFilter?
+
+ @Binds
+ @Named(DREAM_SMARTSPACE_PRECONDITION)
+ abstract fun bindSmartspacePrecondition(
+ lockscreenPrecondition: LockscreenPrecondition?
+ ): SmartspacePrecondition?
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
new file mode 100644
index 000000000000..d3ae198e8e35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -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 com.android.systemui.smartspace.dagger
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.smartspace.dagger.SmartspaceViewComponent.SmartspaceViewModule.PLUGIN
+import dagger.BindsInstance
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Named
+
+@Subcomponent(modules = [SmartspaceViewComponent.SmartspaceViewModule::class])
+interface SmartspaceViewComponent {
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance parent: ViewGroup,
+ @BindsInstance @Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
+ @BindsInstance onAttachListener: View.OnAttachStateChangeListener
+ ): SmartspaceViewComponent
+ }
+
+ fun getView(): BcSmartspaceDataPlugin.SmartspaceView
+
+ @Module
+ object SmartspaceViewModule {
+ const val PLUGIN = "plugin"
+
+ @Provides
+ fun providesSmartspaceView(
+ activityStarter: ActivityStarter,
+ falsingManager: FalsingManager,
+ parent: ViewGroup,
+ @Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
+ onAttachListener: View.OnAttachStateChangeListener
+ ):
+ BcSmartspaceDataPlugin.SmartspaceView {
+ val ssView = plugin.getView(parent)
+ ssView.registerDataProvider(plugin)
+
+ ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
+ override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
+ activityStarter.startActivity(
+ intent,
+ true, /* dismissShade */
+ null, /* launch animator */
+ showOnLockscreen
+ )
+ }
+
+ override fun startPendingIntent(pi: PendingIntent, showOnLockscreen: Boolean) {
+ if (showOnLockscreen) {
+ pi.send()
+ } else {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
+ }
+ })
+ (ssView as View).addOnAttachStateChangeListener(onAttachListener)
+ ssView.setFalsingManager(falsingManager)
+ return ssView
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
new file mode 100644
index 000000000000..6ad490169c17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.smartspace.filters
+
+import android.app.smartspace.SmartspaceTarget
+import android.content.ContentResolver
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.settings.SecureSettings
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen.
+ */
+class LockscreenTargetFilter @Inject constructor(
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ private val execution: Execution,
+ @Main private val handler: Handler,
+ private val contentResolver: ContentResolver,
+ @Main private val uiExecutor: Executor
+) : SmartspaceTargetFilter {
+ private var listeners: MutableSet<SmartspaceTargetFilter.Listener> = mutableSetOf()
+ private var showSensitiveContentForCurrentUser = false
+ set(value) {
+ val existing = field
+ field = value
+ if (existing != field) {
+ listeners.forEach { it.onCriteriaChanged() }
+ }
+ }
+ private var showSensitiveContentForManagedUser = false
+ set(value) {
+ val existing = field
+ field = value
+ if (existing != field) {
+ listeners.forEach { it.onCriteriaChanged() }
+ }
+ }
+
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ updateUserContentSettings()
+ }
+ }
+
+ private var managedUserHandle: UserHandle? = null
+
+ override fun addListener(listener: SmartspaceTargetFilter.Listener) {
+ listeners.add(listener)
+
+ if (listeners.size != 1) {
+ return
+ }
+
+ userTracker.addCallback(userTrackerCallback, uiExecutor)
+
+ contentResolver.registerContentObserver(
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
+
+ updateUserContentSettings()
+ }
+
+ override fun removeListener(listener: SmartspaceTargetFilter.Listener) {
+ listeners.remove(listener)
+
+ if (listeners.isNotEmpty()) {
+ return
+ }
+
+ userTracker.removeCallback(userTrackerCallback)
+ contentResolver.unregisterContentObserver(settingsObserver)
+ }
+
+ override fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ return when (t.userHandle) {
+ userTracker.userHandle -> {
+ !t.isSensitive || showSensitiveContentForCurrentUser
+ }
+ managedUserHandle -> {
+ // Really, this should be "if this managed profile is associated with the current
+ // active user", but we don't have a good way to check that, so instead we cheat:
+ // Only the primary user can have an associated managed profile, so only show
+ // content for the managed profile if the primary user is active
+ userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
+ (!t.isSensitive || showSensitiveContentForManagedUser)
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ updateUserContentSettings()
+ }
+ }
+
+ private fun getWorkProfileUser(): UserHandle? {
+ for (userInfo in userTracker.userProfiles) {
+ if (userInfo.isManagedProfile) {
+ return userInfo.userHandle
+ }
+ }
+ return null
+ }
+
+ private fun updateUserContentSettings() {
+ val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+
+ showSensitiveContentForCurrentUser =
+ secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+
+ managedUserHandle = getWorkProfileUser()
+ val managedId = managedUserHandle?.identifier
+ if (managedId != null) {
+ showSensitiveContentForManagedUser =
+ secureSettings.getIntForUser(setting, 0, managedId) == 1
+ }
+
+ listeners.forEach { it.onCriteriaChanged() }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
new file mode 100644
index 000000000000..1302ec9dbc55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.smartspace.preconditions
+
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.smartspace.SmartspacePrecondition
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.concurrency.Execution
+import javax.inject.Inject
+
+/**
+ * {@link LockscreenPrecondition} covers the conditions that must be met before Smartspace can be
+ * used over lockscreen. These conditions include the device being provisioned with a setup user
+ * and the Smartspace feature flag enabled.
+ */
+class LockscreenPrecondition @Inject constructor(
+ private val featureFlags: FeatureFlags,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val execution: Execution
+) : SmartspacePrecondition {
+ private var listeners = mutableSetOf<SmartspacePrecondition.Listener>()
+
+ private val deviceProvisionedListener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onDeviceProvisionedChanged() {
+ updateDeviceReadiness()
+ }
+
+ override fun onUserSetupChanged() {
+ updateDeviceReadiness()
+ }
+ }
+
+ init {
+ deviceProvisionedController.addCallback(deviceProvisionedListener)
+ }
+
+ var deviceReady: Boolean = false
+ private set
+
+ init {
+ updateDeviceReadiness()
+ }
+
+ private fun updateDeviceReadiness() {
+ if (deviceReady) {
+ return
+ }
+
+ deviceReady = deviceProvisionedController.isDeviceProvisioned &&
+ deviceProvisionedController.isCurrentUserSetup
+
+ if (!deviceReady) {
+ return
+ }
+
+ deviceProvisionedController.removeCallback(deviceProvisionedListener)
+ synchronized(listeners) {
+ listeners.forEach { it.onCriteriaChanged() }
+ }
+ }
+
+ override fun addListener(listener: SmartspacePrecondition.Listener) {
+ synchronized(listeners) {
+ listeners += listener
+ }
+ // Always trigger a targeted callback upon addition of listener.
+ listener.onCriteriaChanged()
+ }
+
+ override fun removeListener(listener: SmartspacePrecondition.Listener) {
+ synchronized(listeners) {
+ listeners -= listener
+ }
+ }
+
+ override fun conditionsMet(): Boolean {
+ execution.assertIsMainThread()
+ return featureFlags.isEnabled(Flags.SMARTSPACE) && deviceReady
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 17f42b1a3a43..1ab03455cca2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -99,15 +99,57 @@ class LockscreenShadeTransitionController @Inject constructor(
internal var pulseHeightAnimator: ValueAnimator? = null
/**
- * Distance that the full shade transition takes in order for scrim to fully transition to
- * the shade (in alpha)
+ * Distance that the full shade transition takes in order to complete.
+ */
+ private var fullTransitionDistance = 0
+
+ /**
+ * Distance that the full transition takes in order for us to fully transition to the shade by
+ * tapping on a button, such as "expand".
+ */
+ private var fullTransitionDistanceByTap = 0
+
+ /**
+ * Distance that the full shade transition takes in order for scrim to fully transition to the
+ * shade (in alpha)
*/
private var scrimTransitionDistance = 0
/**
- * Distance that the full transition takes in order for us to fully transition to the shade
+ * Distance that the full shade transition takes in order for the notification shelf to fully
+ * expand.
*/
- private var fullTransitionDistance = 0
+ private var notificationShelfTransitionDistance = 0
+
+ /**
+ * Distance that the full shade transition takes in order for the Quick Settings to fully fade
+ * and expand.
+ */
+ private var qsTransitionDistance = 0
+
+ /**
+ * Distance that the full shade transition takes in order for the keyguard content on
+ * NotificationPanelViewController to fully fade (e.g. Clock & Smartspace).
+ */
+ private var npvcKeyguardContentAlphaTransitionDistance = 0
+
+ /**
+ * Distance that the full shade transition takes in order for depth of the wallpaper to fully
+ * change.
+ */
+ private var depthControllerTransitionDistance = 0
+
+ /**
+ * Distance that the full shade transition takes in order for the UDFPS Keyguard View to fully
+ * fade.
+ */
+ private var udfpsTransitionDistance = 0
+
+ /**
+ * Used for StatusBar to know that a transition is in progress. At the moment it only checks
+ * whether the progress is > 0, therefore this value is not very important.
+ */
+ private var statusBarTransitionDistance = 0
/**
* Flag to make sure that the dragDownAmount is applied to the listeners even when in the
@@ -130,7 +172,7 @@ class LockscreenShadeTransitionController @Inject constructor(
* The distance until we're showing the notifications when pulsing
*/
val distanceUntilShowingPulsingNotifications
- get() = scrimTransitionDistance
+ get() = fullTransitionDistance
/**
* The udfpsKeyguardViewController if it exists.
@@ -177,10 +219,24 @@ class LockscreenShadeTransitionController @Inject constructor(
}
private fun updateResources() {
+ fullTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_full_transition_distance)
+ fullTransitionDistanceByTap = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_transition_by_tap_distance)
scrimTransitionDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_scrim_transition_distance)
- fullTransitionDistance = context.resources.getDimensionPixelSize(
+ notificationShelfTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_notif_shelf_transition_distance)
+ qsTransitionDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_qs_transition_distance)
+ npvcKeyguardContentAlphaTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
+ depthControllerTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_depth_controller_transition_distance)
+ udfpsTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
+ statusBarTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_status_bar_transition_distance)
useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
@@ -248,6 +304,7 @@ class LockscreenShadeTransitionController @Inject constructor(
// override that
forceApplyAmount = true
// Reset the behavior. At this point the animation is already started
+ logger.logDragDownAmountReset()
dragDownAmount = 0f
forceApplyAmount = false
}
@@ -337,11 +394,16 @@ class LockscreenShadeTransitionController @Inject constructor(
if (field != value || forceApplyAmount) {
field = value
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
- qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
- nsslController.setTransitionToFullShadeAmount(field, qSDragProgress)
+ val notificationShelfProgress =
+ MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
+ nsslController.setTransitionToFullShadeAmount(field, notificationShelfProgress)
+
+ qSDragProgress = MathUtils.saturate(dragDownAmount / qsTransitionDistance)
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
+
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
+
mediaHierarchyManager.setTransitionToFullShadeAmount(field)
transitionToShadeAmountCommon(field)
}
@@ -357,11 +419,23 @@ class LockscreenShadeTransitionController @Inject constructor(
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
val scrimProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
scrimController.setTransitionToFullShadeProgress(scrimProgress)
+
// Fade out all content only visible on the lockscreen
- notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
- depthController.transitionToFullShadeProgress = scrimProgress
- udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
- centralSurfaces.setTransitionToFullShadeProgress(scrimProgress)
+ val npvcProgress =
+ MathUtils.saturate(dragDownAmount / npvcKeyguardContentAlphaTransitionDistance)
+ notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - npvcProgress)
+
+ if (depthControllerTransitionDistance > 0) {
+ val depthProgress =
+ MathUtils.saturate(dragDownAmount / depthControllerTransitionDistance)
+ depthController.transitionToFullShadeProgress = depthProgress
+ }
+
+ val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance)
+ udfpsKeyguardViewController?.setTransitionToFullShadeProgress(udfpsProgress)
+
+ val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance)
+ centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress)
}
private fun setDragDownAmountAnimated(
@@ -404,9 +478,10 @@ class LockscreenShadeTransitionController @Inject constructor(
// be a couple of frames later. if we're setting it to 0, it will use the
// default inset and therefore flicker
dragDownAmount = 1f
- setDragDownAmountAnimated(fullTransitionDistance.toFloat(), delay = delay) {
+ setDragDownAmountAnimated(fullTransitionDistanceByTap.toFloat(), delay = delay) {
// End listener:
// Reset
+ logger.logDragDownAmountReset()
dragDownAmount = 0f
forceApplyAmount = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
deleted file mode 100644
index 5396b8638225..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.communal.CommunalStateController;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * {@link CommunalCoordinator} prevents notifications from showing on the keyguard when the communal
- * view is present.
- */
-@CoordinatorScope
-public class CommunalCoordinator implements Coordinator {
- final Executor mExecutor;
- final CommunalStateController mCommunalStateController;
- final NotificationEntryManager mNotificationEntryManager;
- final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
-
- @Inject
- public CommunalCoordinator(@Main Executor executor,
- NotificationEntryManager notificationEntryManager,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- CommunalStateController communalStateController) {
- mExecutor = executor;
- mNotificationEntryManager = notificationEntryManager;
- mNotificationLockscreenUserManager = notificationLockscreenUserManager;
- mCommunalStateController = communalStateController;
- }
-
- final NotifFilter mFilter = new NotifFilter("CommunalCoordinator") {
- @Override
- public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
- return mCommunalStateController.getCommunalViewShowing();
- }
- };
-
- final CommunalStateController.Callback mStateCallback = new CommunalStateController.Callback() {
- @Override
- public void onCommunalViewShowingChanged() {
- mExecutor.execute(() -> {
- mFilter.invalidateList();
- mNotificationEntryManager.updateNotifications("Communal mode state changed");
- });
- }
- };
-
- @Override
- public void attach(@NonNull NotifPipeline pipeline) {
- pipeline.addPreGroupFilter(mFilter);
- mCommunalStateController.addCallback(mStateCallback);
- if (!pipeline.isNewPipelineEnabled()) {
- mNotificationLockscreenUserManager.addKeyguardNotificationSuppressor(
- entry -> mCommunalStateController.getCommunalViewShowing());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 41b070635d4f..0df2162d3338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -72,9 +72,9 @@ class HeadsUpCoordinator @Inject constructor(
private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
private lateinit var mNotifPipeline: NotifPipeline
private var mNow: Long = -1
-
// notifs we've extended the lifetime for
private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
+ private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
override fun attach(pipeline: NotifPipeline) {
mNotifPipeline = pipeline
@@ -101,10 +101,12 @@ class HeadsUpCoordinator @Inject constructor(
return
}
// Process all non-group adds/updates
- mPostedEntries.values.toList().forEach { posted ->
- if (!posted.entry.sbn.isGroup) {
- handlePostedEntry(posted, "non-group")
- mPostedEntries.remove(posted.key)
+ mHeadsUpManager.modifyHuns { hunMutator ->
+ mPostedEntries.values.toList().forEach { posted ->
+ if (!posted.entry.sbn.isGroup) {
+ handlePostedEntry(posted, hunMutator, "non-group")
+ mPostedEntries.remove(posted.key)
+ }
}
}
}
@@ -114,10 +116,10 @@ class HeadsUpCoordinator @Inject constructor(
* we know that stability and [NotifPromoter]s have been applied, so we can use the location of
* notifications in this list to determine what kind of group alert behavior should happen.
*/
- fun onBeforeFinalizeFilter(list: List<ListEntry>) {
+ fun onBeforeFinalizeFilter(list: List<ListEntry>) = mHeadsUpManager.modifyHuns { hunMutator ->
// Nothing to do if there are no other adds/updates
if (mPostedEntries.isEmpty()) {
- return
+ return@modifyHuns
}
// Calculate a bunch of information about the logical group and the locations of group
// entries in the nearly-finalized shade list. These may be used in the per-group loop.
@@ -138,13 +140,17 @@ class HeadsUpCoordinator @Inject constructor(
// If there is no logical summary, then there is no alert to transfer
if (logicalSummary == null) {
- postedEntries.forEach { handlePostedEntry(it, "logical-summary-missing") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing")
+ }
return@forEach
}
// If summary isn't wanted to be heads up, then there is no alert to transfer
if (!isGoingToShowHunStrict(logicalSummary)) {
- postedEntries.forEach { handlePostedEntry(it, "logical-summary-not-alerting") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-alerting")
+ }
return@forEach
}
@@ -177,7 +183,9 @@ class HeadsUpCoordinator @Inject constructor(
// If there is no child to receive the parent alert, then just handle the posted entries
// and return.
if (childToReceiveParentAlert == null) {
- postedEntries.forEach { handlePostedEntry(it, "no-transfer-target") }
+ postedEntries.forEach {
+ handlePostedEntry(it, hunMutator, scenario = "no-transfer-target")
+ }
return@forEach
}
@@ -189,51 +197,66 @@ class HeadsUpCoordinator @Inject constructor(
if (!isSummaryAttached) {
val summaryUpdateForRemoval = summaryUpdate?.also {
it.shouldHeadsUpEver = false
- } ?: PostedEntry(logicalSummary,
- wasAdded = false,
- wasUpdated = false,
- shouldHeadsUpEver = false,
- shouldHeadsUpAgain = false,
- isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
- isBinding = isEntryBinding(logicalSummary),
+ } ?: PostedEntry(
+ logicalSummary,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = false,
+ shouldHeadsUpAgain = false,
+ isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
+ isBinding = isEntryBinding(logicalSummary),
)
// If we transfer the alert and the summary isn't even attached, that means we
// should ensure the summary is no longer alerting, so we remove it here.
- handlePostedEntry(summaryUpdateForRemoval, "detached-summary-remove-alert")
- } else if (summaryUpdate!=null) {
- mLogger.logPostedEntryWillNotEvaluate(summaryUpdate, "attached-summary-transferred")
+ handlePostedEntry(
+ summaryUpdateForRemoval,
+ hunMutator,
+ scenario = "detached-summary-remove-alert")
+ } else if (summaryUpdate != null) {
+ mLogger.logPostedEntryWillNotEvaluate(
+ summaryUpdate,
+ reason = "attached-summary-transferred")
}
// Handle all posted entries -- if the child receiving the parent's alert is in the
// list, then set its flags to ensure it alerts.
var didAlertChildToReceiveParentAlert = false
postedEntries.asSequence()
- .filter { it.key != logicalSummary.key }
- .forEach { postedEntry ->
- if (childToReceiveParentAlert.key == postedEntry.key) {
- // Update the child's posted update so that it
- postedEntry.shouldHeadsUpEver = true
- postedEntry.shouldHeadsUpAgain = true
- handlePostedEntry(postedEntry, "child-alert-transfer-target-$targetType")
- didAlertChildToReceiveParentAlert = true
- } else {
- handlePostedEntry(postedEntry, "child-alert-non-target")
+ .filter { it.key != logicalSummary.key }
+ .forEach { postedEntry ->
+ if (childToReceiveParentAlert.key == postedEntry.key) {
+ // Update the child's posted update so that it
+ postedEntry.shouldHeadsUpEver = true
+ postedEntry.shouldHeadsUpAgain = true
+ handlePostedEntry(
+ postedEntry,
+ hunMutator,
+ scenario = "child-alert-transfer-target-$targetType")
+ didAlertChildToReceiveParentAlert = true
+ } else {
+ handlePostedEntry(
+ postedEntry,
+ hunMutator,
+ scenario = "child-alert-non-target")
+ }
}
- }
// If the child receiving the alert was not updated on this tick (which can happen in a
// standard alert transfer scenario), then construct an update so that we can apply it.
if (!didAlertChildToReceiveParentAlert) {
val posted = PostedEntry(
- childToReceiveParentAlert,
- wasAdded = false,
- wasUpdated = false,
- shouldHeadsUpEver = true,
- shouldHeadsUpAgain = true,
- isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
- isBinding = isEntryBinding(childToReceiveParentAlert),
+ childToReceiveParentAlert,
+ wasAdded = false,
+ wasUpdated = false,
+ shouldHeadsUpEver = true,
+ shouldHeadsUpAgain = true,
+ isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
+ isBinding = isEntryBinding(childToReceiveParentAlert),
)
- handlePostedEntry(posted, "non-posted-child-alert-transfer-target-$targetType")
+ handlePostedEntry(
+ posted,
+ hunMutator,
+ scenario = "non-posted-child-alert-transfer-target-$targetType")
}
}
// After this method runs, all posted entries should have been handled (or skipped).
@@ -292,9 +315,7 @@ class HeadsUpCoordinator @Inject constructor(
}
}
- private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
-
- fun handlePostedEntry(posted: PostedEntry, scenario: String) {
+ private fun handlePostedEntry(posted: PostedEntry, hunMutator: HunMutator, scenario: String) {
mLogger.logPostedEntryWillEvaluate(posted, scenario)
if (posted.wasAdded) {
if (posted.shouldHeadsUpEver) {
@@ -308,12 +329,12 @@ class HeadsUpCoordinator @Inject constructor(
// If alerting, we need to post an update. Otherwise we're still binding,
// and we can just let that finish.
if (posted.isAlerting) {
- mHeadsUpManager.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+ hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain)
}
} else {
if (posted.isAlerting) {
// We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ hunMutator.removeNotification(posted.key, false /*removeImmediately*/)
} else {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
@@ -366,7 +387,7 @@ class HeadsUpCoordinator @Inject constructor(
val shouldHeadsUpAgain = shouldHunAgain(entry)
val isAlerting = mHeadsUpManager.isAlerting(entry.key)
val isBinding = isEntryBinding(entry)
- mPostedEntries.compute(entry.key) { _, value ->
+ val posted = mPostedEntries.compute(entry.key) { _, value ->
value?.also { update ->
update.wasUpdated = true
update.shouldHeadsUpEver = update.shouldHeadsUpEver || shouldHeadsUpEver
@@ -383,6 +404,18 @@ class HeadsUpCoordinator @Inject constructor(
isBinding = isBinding,
)
}
+ // Handle cancelling alerts here, rather than in the OnBeforeFinalizeFilter, so that
+ // work can be done before the ShadeListBuilder is run. This prevents re-entrant
+ // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
+ if (posted?.shouldHeadsUpEver == false) {
+ if (posted.isAlerting) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ } else if (posted.isBinding) {
+ // Don't let the bind finish
+ cancelHeadsUpBind(posted.entry)
+ }
+ }
}
/**
@@ -543,3 +576,43 @@ private enum class GroupLocation { Detached, Isolated, Summary, Child }
private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
getOrDefault(key, GroupLocation.Detached)
+
+/**
+ * Invokes the given block with a [HunMutator] that defers all HUN removals. This ensures that the
+ * HeadsUpManager is notified of additions before removals, which prevents a glitch where the
+ * HeadsUpManager temporarily believes that nothing is alerting, causing bad re-entrant behavior.
+ */
+private fun <R> HeadsUpManager.modifyHuns(block: (HunMutator) -> R): R {
+ val mutator = HunMutatorImpl(this)
+ return block(mutator).also { mutator.commitModifications() }
+}
+
+/** Mutates the HeadsUp state of notifications. */
+private interface HunMutator {
+ fun updateNotification(key: String, alert: Boolean)
+ fun removeNotification(key: String, releaseImmediately: Boolean)
+}
+
+/**
+ * [HunMutator] implementation that defers removing notifications from the HeadsUpManager until
+ * after additions/updates.
+ */
+private class HunMutatorImpl(private val headsUpManager: HeadsUpManager) : HunMutator {
+ private val deferred = mutableListOf<Pair<String, Boolean>>()
+
+ override fun updateNotification(key: String, alert: Boolean) {
+ headsUpManager.updateNotification(key, alert)
+ }
+
+ override fun removeNotification(key: String, releaseImmediately: Boolean) {
+ val args = Pair(key, releaseImmediately)
+ deferred.add(args)
+ }
+
+ fun commitModifications() {
+ deferred.forEach { (key, releaseImmediately) ->
+ headsUpManager.removeNotification(key, releaseImmediately)
+ }
+ deferred.clear()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index be20df460bb0..985914acb2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -45,7 +45,6 @@ class NotifCoordinatorsImpl @Inject constructor(
bubbleCoordinator: BubbleCoordinator,
headsUpCoordinator: HeadsUpCoordinator,
gutsCoordinator: GutsCoordinator,
- communalCoordinator: CommunalCoordinator,
conversationCoordinator: ConversationCoordinator,
debugModeCoordinator: DebugModeCoordinator,
groupCountCoordinator: GroupCountCoordinator,
@@ -87,7 +86,6 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(appOpsCoordinator)
mCoordinators.add(deviceProvisionedCoordinator)
mCoordinators.add(bubbleCoordinator)
- mCoordinators.add(communalCoordinator)
mCoordinators.add(debugModeCoordinator)
mCoordinators.add(conversationCoordinator)
mCoordinators.add(groupCountCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index f40a3c7186e6..3b22f2a86bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -102,6 +102,16 @@ public class AmbientState {
/** Whether we are swiping up. */
private boolean mIsSwipingUp;
+ /** Whether we are flinging the shade open or closed. */
+ private boolean mIsFlinging;
+
+ /**
+ * Whether we need to do a fling down after swiping up on lockscreen.
+ * True right after we swipe up on lockscreen and have not finished the fling down that follows.
+ * False when we stop flinging or leave lockscreen.
+ */
+ private boolean mNeedFlingAfterLockscreenSwipeUp = false;
+
/**
* @return Height of the notifications panel without top padding when expansion completes.
*/
@@ -142,6 +152,10 @@ public class AmbientState {
* @param isSwipingUp Whether we are swiping up.
*/
public void setSwipingUp(boolean isSwipingUp) {
+ if (!isSwipingUp && mIsSwipingUp) {
+ // Just stopped swiping up.
+ mNeedFlingAfterLockscreenSwipeUp = true;
+ }
mIsSwipingUp = isSwipingUp;
}
@@ -153,6 +167,17 @@ public class AmbientState {
}
/**
+ * @param isFlinging Whether we are flinging the shade open or closed.
+ */
+ public void setIsFlinging(boolean isFlinging) {
+ if (isOnKeyguard() && !isFlinging && mIsFlinging) {
+ // Just stopped flinging.
+ mNeedFlingAfterLockscreenSwipeUp = false;
+ }
+ mIsFlinging = isFlinging;
+ }
+
+ /**
* @return Fraction of shade expansion.
*/
public float getExpansionFraction() {
@@ -459,6 +484,9 @@ public class AmbientState {
}
public void setStatusBarState(int statusBarState) {
+ if (mStatusBarState != StatusBarState.KEYGUARD) {
+ mNeedFlingAfterLockscreenSwipeUp = false;
+ }
mStatusBarState = statusBarState;
}
@@ -522,6 +550,13 @@ public class AmbientState {
}
/**
+ * @return Whether we need to do a fling down after swiping up on lockscreen.
+ */
+ public boolean isFlingingAfterSwipeUpOnLockscreen() {
+ return mIsFlinging && mNeedFlingAfterLockscreenSwipeUp;
+ }
+
+ /**
* @return whether a view is dozing and not pulsing right now
*/
public boolean isDozingAndNotPulsing(ExpandableView view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index efe559a64cc4..5b9dbd0f3361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -202,9 +202,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private float mQsExpansionFraction;
private final int mSplitShadeMinContentHeight;
- /** Whether we are flinging the shade open or closed. */
- private boolean mIsFlinging;
-
/**
* The algorithm which calculates the properties for our children
*/
@@ -407,7 +404,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private float mBackgroundXFactor = 1f;
- private boolean mQsExpanded;
+ /**
+ * Indicates QS are full screen and pushing notifications out of the screen.
+ * It's different from QS just being expanded as in split shade QS can be expanded and
+ * still don't take full screen nor influence notifications.
+ */
+ private boolean mQsFullScreen;
private boolean mForwardScrollable;
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
@@ -1133,7 +1135,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
+ mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
? getLayoutMinHeight() : 0);
}
@@ -1263,13 +1265,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
- * @return Whether we should skip stack height update for lockscreen swipe-up or unlock hint.
+ * @return Whether we should skip stack height updates.
+ * True when
+ * 1) Unlock hint is running
+ * 2) Swiping up on lockscreen or flinging down after swipe up
*/
private boolean shouldSkipHeightUpdate() {
- // After the user swipes up on lockscreen and lets go,
- // {@link PanelViewController) flings the shade back down.
- return mAmbientState.isOnKeyguard() && (
- mAmbientState.isUnlockHintRunning() || mAmbientState.isSwipingUp() || mIsFlinging);
+ return mAmbientState.isOnKeyguard()
+ && (mAmbientState.isUnlockHintRunning()
+ || mAmbientState.isSwipingUp()
+ || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
}
/**
@@ -1361,7 +1366,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
translationY = 0;
if (mShouldShowShelfOnly) {
stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
- } else if (mQsExpanded) {
+ } else if (mQsFullScreen) {
int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
@@ -2318,7 +2323,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateScrollability() {
- boolean scrollable = !mQsExpanded && getScrollRange() > 0;
+ boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
setFocusable(scrollable);
@@ -4839,14 +4844,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setQsExpanded(boolean qsExpanded) {
- mQsExpanded = qsExpanded;
+ public void setQsFullScreen(boolean qsFullScreen) {
+ mQsFullScreen = qsFullScreen;
updateAlgorithmLayoutMinHeight();
updateScrollability();
}
- boolean isQsExpanded() {
- return mQsExpanded;
+ boolean isQsFullScreen() {
+ return mQsFullScreen;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5009,13 +5014,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAmbientState.setUnlockHintRunning(running);
}
- /**
- * @param isFlinging Whether we are flinging the shade open or closed.
- */
- public void setIsFlinging(boolean isFlinging) {
- mIsFlinging = isFlinging;
- }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
@@ -5814,10 +5812,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipeHelper.resetExposedMenuView(animate, force);
}
- boolean isUsingSplitNotificationShade() {
- return mShouldUseSplitNotificationShade;
- }
-
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index df6b8f59fc36..7df8e7df486e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -111,13 +111,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -1086,8 +1086,8 @@ public class NotificationStackScrollLayoutController {
}
}
- public void setQsExpanded(boolean expanded) {
- mView.setQsExpanded(expanded);
+ public void setQsFullScreen(boolean fullScreen) {
+ mView.setQsFullScreen(fullScreen);
updateShowEmptyShadeView();
}
@@ -1181,13 +1181,6 @@ public class NotificationStackScrollLayoutController {
mView.setUnlockHintRunning(running);
}
- /**
- * @param isFlinging Whether we are flinging the shade open or close.
- */
- public void setIsFlinging(boolean isFlinging) {
- mView.setIsFlinging(isFlinging);
- }
-
public boolean isFooterViewNotGone() {
return mView.isFooterViewNotGone();
}
@@ -1211,7 +1204,7 @@ public class NotificationStackScrollLayoutController {
public void updateShowEmptyShadeView() {
Trace.beginSection("NSSLC.updateShowEmptyShadeView");
mShowEmptyShadeView = mBarState != KEYGUARD
- && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
+ && !mView.isQsFullScreen()
&& getVisibleNotificationCount() == 0;
mView.updateEmptyShadeView(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index fe637c14ee33..4bf944ae13c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -20,6 +20,8 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD;
import android.annotation.IntDef;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
@@ -344,7 +346,15 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
@Override
- public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType,
+ int acquireInfo) {
+ if (BiometricSourceType.FINGERPRINT == biometricSourceType
+ && acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
+ return;
+ } else if (BiometricSourceType.FACE == biometricSourceType
+ && acquireInfo != BiometricFaceConstants.FACE_ACQUIRED_GOOD) {
+ return;
+ }
Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
releaseBiometricWakeLock();
if (isWakeAndUnlock()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 95a465989299..a7f950eaf167 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -2890,7 +2890,8 @@ public class CentralSurfaces extends CoreStartable implements
// turned off fully.
boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
&& (!mDeviceInteractive || (isGoingToSleep()
- && (isScreenFullyOff() || mKeyguardStateController.isShowing())));
+ && (isScreenFullyOff()
+ || (mKeyguardStateController.isShowing() && !isOccluded()))));
boolean isWakingAndOccluded = isOccluded() && isWaking();
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
|| keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
@@ -3769,7 +3770,7 @@ public class CentralSurfaces extends CoreStartable implements
});
} else if (mDozing && !unlocking) {
mScrimController.transitionTo(ScrimState.AOD);
- } else if (mKeyguardStateController.isShowing() && !unlocking) {
+ } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 541aeab22a80..dc1af362ec23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -32,7 +32,6 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -1109,8 +1108,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
Intent intent = mQRCodeScannerController.getIntent();
if (intent != null) {
try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
+ ActivityTaskManager.getService().startActivityAsUser(
+ null, getContext().getBasePackageName(),
+ getContext().getAttributionTag(), intent,
+ intent.resolveTypeIfNeeded(getContext().getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
+ UserHandle.CURRENT.getIdentifier());
+ } catch (RemoteException e) {
// This is unexpected. Nonetheless, just log the error and prevent the UI from
// crashing
Log.e(TAG, "Unexpected intent: " + intent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
index 868efa027f40..02b235493715 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -44,7 +44,7 @@ class LSShadeTransitionLogger @Inject constructor(
fun logDragDownAborted() {
buffer.log(TAG, LogLevel.INFO, {}, {
- "The drag down was reset"
+ "The drag down was aborted and reset to 0f."
})
}
@@ -82,6 +82,12 @@ class LSShadeTransitionLogger @Inject constructor(
LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
}
+ fun logDragDownAmountReset() {
+ buffer.log(TAG, LogLevel.DEBUG, {}, {
+ "The drag down amount has been reset to 0f."
+ })
+ }
+
fun logDefaultGoToFullShadeAnimation(delay: Long) {
buffer.log(TAG, LogLevel.DEBUG, {
long1 = delay
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e5c88b571f32..cc8a70388ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -120,13 +120,6 @@ import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.communal.CommunalHostView;
-import com.android.systemui.communal.CommunalHostViewController;
-import com.android.systemui.communal.CommunalHostViewPositionAlgorithm;
-import com.android.systemui.communal.CommunalSource;
-import com.android.systemui.communal.CommunalSourceMonitor;
-import com.android.systemui.communal.CommunalStateController;
-import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -209,7 +202,6 @@ import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -308,13 +300,10 @@ public class NotificationPanelViewController extends PanelViewController {
private final PulseExpansionHandler mPulseExpansionHandler;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final CommunalSourceMonitor mCommunalSourceMonitor;
- private final CommunalStateController mCommunalStateController;
private final ConversationNotificationManager mConversationNotificationManager;
private final AuthController mAuthController;
private final MediaHierarchyManager mMediaHierarchyManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final CommunalViewComponent.Factory mCommunalViewComponentFactory;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
@@ -350,8 +339,6 @@ public class NotificationPanelViewController extends PanelViewController {
@VisibleForTesting QS mQs;
private FrameLayout mQsFrame;
private QsFrameTranslateController mQsFrameTranslateController;
- @Nullable
- private CommunalHostViewController mCommunalViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@@ -364,8 +351,6 @@ public class NotificationPanelViewController extends PanelViewController {
private VelocityTracker mQsVelocityTracker;
private boolean mQsTracking;
- private CommunalHostView mCommunalView;
-
/**
* If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
* the expansion for quick settings.
@@ -373,6 +358,12 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mConflictingQsExpansionGesture;
private boolean mPanelExpanded;
+
+ /**
+ * Indicates that QS is in expanded state which can happen by:
+ * - single pane shade: expanding shade and then expanding QS
+ * - split shade: just expanding shade (QS are expanded automatically)
+ */
private boolean mQsExpanded;
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
@@ -413,16 +404,14 @@ public class NotificationPanelViewController extends PanelViewController {
private final KeyguardClockPositionAlgorithm.Result
mClockPositionResult =
new KeyguardClockPositionAlgorithm.Result();
- private final CommunalHostViewPositionAlgorithm
- mCommunalPositionAlgorithm =
- new CommunalHostViewPositionAlgorithm();
- private final CommunalHostViewPositionAlgorithm.Result
- mCommunalPositionResult =
- new CommunalHostViewPositionAlgorithm.Result();
private boolean mIsExpanding;
private boolean mBlockTouches;
- // Used for two finger gesture as well as accessibility shortcut to QS.
+
+ /**
+ * Determines if QS should be already expanded when expanding shade.
+ * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
+ */
private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
private String mHeaderDebugInfo;
@@ -523,10 +512,6 @@ public class NotificationPanelViewController extends PanelViewController {
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
- private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback;
-
- private WeakReference<CommunalSource> mCommunalSource;
-
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
@@ -686,32 +671,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
- private final CommunalStateController.Callback mCommunalStateCallback =
- new CommunalStateController.Callback() {
- @Override
- public void onCommunalViewShowingChanged() {
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- mBarState,
- mKeyguardStateController.isKeyguardFadingAway(),
- mStatusBarStateController.goingToFullShade(),
- mBarState);
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
- mBarState,
- mKeyguardStateController.isKeyguardFadingAway(),
- mStatusBarStateController.goingToFullShade(),
- mBarState);
- }
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
- mBarState,
- mKeyguardStateController.isKeyguardFadingAway(),
- mStatusBarStateController.goingToFullShade(),
- mBarState);
- }
- }
- };
-
private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() {
@Override
public void onDoubleTapRequired() {
@@ -737,7 +696,6 @@ public class NotificationPanelViewController extends PanelViewController {
FalsingCollector falsingCollector,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
- CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarWindowStateController statusBarWindowStateController,
@@ -747,7 +705,7 @@ public class NotificationPanelViewController extends PanelViewController {
LatencyTracker latencyTracker, PowerManager powerManager,
AccessibilityManager accessibilityManager, @DisplayId int displayId,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- CommunalSourceMonitor communalSourceMonitor, MetricsLogger metricsLogger,
+ MetricsLogger metricsLogger,
ActivityManager activityManager,
ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
@@ -761,7 +719,6 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
- CommunalViewComponent.Factory communalViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
@@ -831,8 +788,6 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
- mCommunalStateController = communalStateController;
- mCommunalViewComponentFactory = communalViewComponentFactory;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
mDepthController = notificationShadeDepthController;
@@ -877,7 +832,6 @@ public class NotificationPanelViewController extends PanelViewController {
mThemeResId = mView.getContext().getThemeResId();
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
- mCommunalSourceMonitor = communalSourceMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
lockscreenShadeTransitionController.setNotificationPanelController(this);
DynamicPrivacyControlListener
@@ -924,9 +878,6 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
- mCommunalSourceMonitorCallback = (source) -> {
- mUiExecutor.execute(() -> setCommunalSource(source));
- };
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
@@ -962,7 +913,6 @@ public class NotificationPanelViewController extends PanelViewController {
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
- mCommunalView = mView.findViewById(R.id.communal_host);
FrameLayout userAvatarContainer = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -984,20 +934,11 @@ public class NotificationPanelViewController extends PanelViewController {
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
- if (mCommunalView != null) {
- CommunalViewComponent communalViewComponent =
- mCommunalViewComponentFactory.build(mCommunalView);
- mCommunalViewController =
- communalViewComponent.getCommunalHostViewController();
- mCommunalViewController.init();
- }
-
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
userAvatarContainer,
- keyguardUserSwitcherView,
- mCommunalView);
+ keyguardUserSwitcherView);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
@@ -1088,8 +1029,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
FrameLayout userAvatarView,
- KeyguardUserSwitcherView keyguardUserSwitcherView,
- CommunalHostView communalView) {
+ KeyguardUserSwitcherView keyguardUserSwitcherView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
@@ -1282,7 +1222,7 @@ public class NotificationPanelViewController extends PanelViewController {
showKeyguardUserSwitcher /* enabled */);
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
- keyguardUserSwitcherView, mCommunalView);
+ keyguardUserSwitcherView);
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1428,10 +1368,6 @@ public class NotificationPanelViewController extends PanelViewController {
int stackScrollerPadding;
boolean onKeyguard = isOnKeyguard();
- if (onKeyguard) {
- updateCommunalViewAppearance();
- }
-
if (onKeyguard || forceClockUpdate) {
updateClockAppearance();
}
@@ -1457,22 +1393,6 @@ public class NotificationPanelViewController extends PanelViewController {
mAnimateNextPositionUpdate = false;
}
- private void updateCommunalViewAppearance() {
- if (mCommunalViewController == null) {
- return;
- }
-
- float expandedFraction =
- mScreenOffAnimationController.shouldExpandNotifications()
- ? 1.0f : getExpandedFraction();
- mCommunalPositionAlgorithm.setup(expandedFraction, mCommunalView.getHeight());
- mCommunalPositionAlgorithm.run(mCommunalPositionResult);
- boolean animate =
- mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending()
- || mAnimateNextPositionUpdate;
- mCommunalViewController.updatePosition(mCommunalPositionResult.communalY, animate);
- }
-
private void updateClockAppearance() {
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
@@ -1547,9 +1467,8 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- boolean hasCommunalSurface = mCommunalSource != null && mCommunalSource.get() != null;
- boolean shouldBeCentered = !mShouldUseSplitNotificationShade
- || (!hasVisibleNotifications && !hasCommunalSurface) || mDozing;
+ boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications
+ || mDozing;
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -1558,6 +1477,11 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
if (animate) {
ChangeBounds transition = new ChangeBounds();
+ if (mShouldUseSplitNotificationShade) {
+ // Excluding media from the transition on split-shade, as it doesn't transition
+ // horizontally properly.
+ transition.excludeTarget(R.id.status_view_media_container, true);
+ }
transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
@@ -1579,11 +1503,6 @@ public class NotificationPanelViewController extends PanelViewController {
* @return the maximum keyguard notifications that can fit on the screen
*/
private int computeMaxKeyguardNotifications() {
- // Do not show any notifications on the keyguard if a communal source is set.
- if (mCommunalSource != null && mCommunalSource.get() != null) {
- return 0;
- }
-
float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
int notificationPadding = Math.max(
1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
@@ -1707,12 +1626,6 @@ public class NotificationPanelViewController extends PanelViewController {
return true;
}
- private void updateCommunal() {
- if (mCommunalViewController != null) {
- mCommunalViewController.setAlpha(mKeyguardOnlyContentAlpha);
- }
- }
-
private void updateClock() {
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
@@ -1786,11 +1699,16 @@ public class NotificationPanelViewController extends PanelViewController {
if (mQsExpanded) {
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
}
super.collapse(delayed, speedUpFactor);
}
+ private void setShowShelfOnly(boolean shelfOnly) {
+ mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
+ shelfOnly && !mShouldUseSplitNotificationShade);
+ }
+
public void closeQs() {
cancelQsAnimation();
setQsExpansion(mQsMinExpansionHeight);
@@ -1827,7 +1745,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void expandWithQs() {
if (isQsExpansionEnabled()) {
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
}
if (isFullyCollapsed()) {
expand(true /* animate */);
@@ -1860,14 +1778,14 @@ public class NotificationPanelViewController extends PanelViewController {
mHeadsUpTouchHelper.notifyFling(!expand);
mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
- mNotificationStackScrollLayoutController.setIsFlinging(true);
+ mAmbientState.setIsFlinging(true);
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@Override
protected void onFlingEnd(boolean cancelled) {
super.onFlingEnd(cancelled);
- mNotificationStackScrollLayoutController.setIsFlinging(false);
+ mAmbientState.setIsFlinging(false);
}
private boolean onQsIntercept(MotionEvent event) {
@@ -2026,7 +1944,15 @@ public class NotificationPanelViewController extends PanelViewController {
mFalsingManager.isFalseTouch(QS_COLLAPSE);
}
- flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
+ int flingType;
+ if (expandsQs && !isCancelMotionEvent) {
+ flingType = FLING_EXPAND;
+ } else if (mShouldUseSplitNotificationShade) {
+ flingType = FLING_HIDE;
+ } else {
+ flingType = FLING_COLLAPSE;
+ }
+ flingSettings(vel, flingType);
}
private void logQsSwipeDown(float y) {
@@ -2091,8 +2017,10 @@ public class NotificationPanelViewController extends PanelViewController {
return false;
}
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
- && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) {
+ boolean collapsedQs = !mQsExpanded && !mShouldUseSplitNotificationShade;
+ boolean expandedShadeCollapsedQs = getExpandedFraction() == 1f && mBarState != KEYGUARD
+ && collapsedQs && isQsExpansionEnabled();
+ if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
traceQsJank(true /* startTracing */, false /* wasCancelled */);
@@ -2107,7 +2035,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
- if (!mConflictingQsExpansionGesture) {
+ if (!mConflictingQsExpansionGesture && !mShouldUseSplitNotificationShade) {
return true;
}
}
@@ -2121,7 +2049,7 @@ public class NotificationPanelViewController extends PanelViewController {
< mStatusBarMinHeight) {
mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ setShowShelfOnly(true);
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
@@ -2192,6 +2120,9 @@ public class NotificationPanelViewController extends PanelViewController {
if (!isFullyCollapsed()) {
return;
}
+ if (mShouldUseSplitNotificationShade) {
+ mQsExpandImmediate = true;
+ }
mExpectingSynthesizedDown = true;
onTrackingStarted();
updatePanelExpanded();
@@ -2398,12 +2329,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void updateQsState() {
- mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
+ boolean qsFullScreen = mQsExpanded && !mShouldUseSplitNotificationShade;
+ mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
mNotificationStackScrollLayoutController.setScrollingEnabled(
- mBarState != KEYGUARD
- && (!mQsExpanded
- || mQsExpansionFromOverscroll
- || mShouldUseSplitNotificationShade));
+ mBarState != KEYGUARD && (!qsFullScreen || mQsExpansionFromOverscroll));
if (mKeyguardUserSwitcherController != null && mQsExpanded
&& !mStackScrollerOverscrolling) {
@@ -2448,7 +2377,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQsExpansion() {
if (mQs == null) return;
final float squishiness;
- if (mQsExpandImmediate || mQsExpanded) {
+ if ((mQsExpandImmediate || mQsExpanded) && !mShouldUseSplitNotificationShade) {
squishiness = 1;
} else if (mLockscreenShadeTransitionController.getQSDragProgress() > 0) {
squishiness = mLockscreenShadeTransitionController.getQSDragProgress();
@@ -2483,10 +2412,6 @@ public class NotificationPanelViewController extends PanelViewController {
mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
-
- if (mCommunalViewController != null) {
- mCommunalViewController.updateQsExpansion(qsExpansionFraction);
- }
}
private void onStackYChanged(boolean shouldAnimate) {
@@ -2849,10 +2774,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
mTransitionToFullShadeQSPosition = position;
updateQsExpansion();
-
- if (mCommunalViewController != null) {
- mCommunalViewController.updateShadeExpansion(mTransitioningToFullShadeProgress);
- }
}
/**
@@ -2876,7 +2797,6 @@ public class NotificationPanelViewController extends PanelViewController {
updateKeyguardBottomAreaAlpha();
}
updateClock();
- updateCommunal();
}
private void trackMovement(MotionEvent event) {
@@ -3305,7 +3225,7 @@ public class NotificationPanelViewController extends PanelViewController {
setListening(true);
}
mQsExpandImmediate = false;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
+ setShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
updateTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
@@ -3361,9 +3281,7 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimController.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
- if (!mShouldUseSplitNotificationShade) {
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
- }
+ setShowShelfOnly(true);
}
if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
mAffordanceHelper.animateHideLeftRightIcon();
@@ -4068,10 +3986,6 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.runAfterAnimationFinished(r);
}
- public void setScrollingEnabled(boolean b) {
- mNotificationStackScrollLayoutController.setScrollingEnabled(b);
- }
-
private Runnable mHideExpandedRunnable;
private final Runnable mMaybeHideExpandedRunnable = new Runnable() {
@Override
@@ -4756,14 +4670,6 @@ public class NotificationPanelViewController extends PanelViewController {
goingToFullShade,
mBarState);
- if (mCommunalViewController != null) {
- mCommunalViewController.setKeyguardStatusViewVisibility(
- statusBarState,
- keyguardFadingAway,
- goingToFullShade,
- mBarState);
- }
-
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
mBarState = statusBarState;
@@ -4891,25 +4797,6 @@ public class NotificationPanelViewController extends PanelViewController {
setExpandedFraction(1f);
}
- private void setCommunalSource(WeakReference<CommunalSource> source) {
- CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null;
-
- if (existingSource != null) {
- mCommunalViewController.show(null /*source*/);
- }
-
- mCommunalSource = source;
-
- CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null;
- // Set source and register callback
- if (currentSource != null && mCommunalViewController != null) {
- mCommunalViewController.show(source);
- }
-
- updateKeyguardStatusViewAlignment(true /*animate*/);
- updateMaxDisplayedNotifications(true /*recompute*/);
- }
-
/**
* Sets the overstretch amount in raw pixels when dragging down.
*/
@@ -4927,7 +4814,6 @@ public class NotificationPanelViewController extends PanelViewController {
.addTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
- mCommunalSourceMonitor.addCallback(mCommunalSourceMonitorCallback);
// Theme might have changed between inflating this view and attaching it to the
// window, so
// force a call to onThemeChanged
@@ -4935,7 +4821,6 @@ public class NotificationPanelViewController extends PanelViewController {
mFalsingManager.addTapListener(mFalsingTapListener);
mKeyguardIndicationController.init();
registerSettingsChangeListener();
- mCommunalStateController.addCallback(mCommunalStateCallback);
}
@Override
@@ -4945,9 +4830,7 @@ public class NotificationPanelViewController extends PanelViewController {
.removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
- mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback);
mFalsingManager.removeTapListener(mFalsingTapListener);
- mCommunalStateController.removeCallback(mCommunalStateCallback);
}
}
@@ -5013,7 +4896,11 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSMinHeight() {
float previousMin = mQsMinExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ if (mKeyguardShowing || mShouldUseSplitNotificationShade) {
+ mQsMinExpansionHeight = 0;
+ } else {
+ mQsMinExpansionHeight = mQs.getQsMinExpansionHeight();
+ }
if (mQsExpansionHeight == previousMin) {
mQsExpansionHeight = mQsMinExpansionHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 7c1775ed2a5e..1d560c4592a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -397,6 +397,7 @@ public abstract class PanelViewController {
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
mTrackingPointer = -1;
+ mAmbientState.setSwipingUp(false);
if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialTouchX) > mTouchSlop
|| Math.abs(y - mInitialTouchY) > mTouchSlop
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
@@ -460,7 +461,6 @@ public abstract class PanelViewController {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
- mAmbientState.setSwipingUp(false);
mVelocityTracker.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a3c795f36d5d..419661b766d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -773,7 +773,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
// We're unoccluding the keyguard and don't want to have a bright flash.
- mNotificationsAlpha = mScrimBehindAlphaKeyguard;
+ mNotificationsAlpha = ScrimState.KEYGUARD.getNotifAlpha();
mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 7e2488f1dfab..e8bf89a6a90a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -35,7 +35,6 @@ import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
-import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
@@ -119,7 +118,6 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
@Main Resources resources,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
- CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
FalsingManager falsingManager,
ConfigurationController configurationController,
@@ -138,10 +136,9 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
mFalsingManager = falsingManager;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
- screenOffAnimationController, /* animateYPos= */ false,
- /* visibleOnCommunal= */ false);
+ screenOffAnimationController, /* animateYPos= */ false);
mUserSwitchDialogController = userSwitchDialogController;
mUiEventLogger = uiEventLogger;
}
@@ -270,7 +267,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
drawable = new CircleFramedDrawable(mCurrentUser.picture, avatarSize);
}
- Drawable bg = mContext.getDrawable(R.drawable.kg_bg_avatar);
+ Drawable bg = mContext.getDrawable(R.drawable.user_avatar_bg);
drawable = new LayerDrawable(new Drawable[]{bg, drawable});
return drawable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 04a6a114a07d..03ab888d1253 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -42,7 +42,6 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -158,7 +157,6 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
LayoutInflater layoutInflater,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
- CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -174,10 +172,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mAdapter = new KeyguardUserAdapter(mContext, resources, layoutInflater,
mUserSwitcherController, this);
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
keyguardStateController, dozeParameters,
- screenOffAnimationController, /* animateYPos= */ false,
- /* visibleOnCommunal= */ false);
+ screenOffAnimationController, /* animateYPos= */ false);
mBackground = new KeyguardUserSwitcherScrim(context);
}
@@ -543,7 +540,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
}
drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
- Drawable bg = mContext.getDrawable(R.drawable.kg_bg_avatar);
+ Drawable bg = mContext.getDrawable(R.drawable.user_avatar_bg);
drawable = new LayerDrawable(new Drawable[]{bg, drawable});
return drawable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ddc907666f1c..fa26a356f191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -982,9 +982,9 @@ public class UserSwitcherController implements Dumpable {
protected static Drawable getIconDrawable(Context context, UserRecord item) {
int iconRes;
if (item.isAddUser) {
- iconRes = R.drawable.ic_account_circle;
- } else if (item.isGuest) {
iconRes = R.drawable.ic_account_circle_filled;
+ } else if (item.isGuest) {
+ iconRes = R.drawable.ic_account_circle;
} else if (item.isAddSupervisedUser) {
iconRes = R.drawable.ic_add_supervised_user;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 71d8e3344937..7e3bce589f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -199,10 +199,13 @@ public class Utils {
/**
* Gets the {@link R.dimen#split_shade_header_height}.
*
- * Currently, it's the same as {@link com.android.internal.R.dimen#quick_qs_offset_height}.
+ * It should be fine to not ignore cutouts as split shade might not want to react to them:
+ * for split shade header, which is only on bigger screens, either cutout won't be a problem
+ * (it's usually centered and in split shade that's likely empty area) or we probably want to
+ * handle it differently.
*/
public static int getSplitShadeStatusBarHeight(Context context) {
- return SystemBarUtils.getQuickQsOffsetHeight(context);
+ return context.getResources().getDimensionPixelSize(R.dimen.split_shade_header_height);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
index 2ee7b20c1f93..0d47d73153c1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
@@ -24,8 +24,6 @@ import android.content.IntentFilter;
import android.os.PatternMatcher;
import android.util.Log;
-import com.android.systemui.communal.CommunalSource;
-
import com.google.android.collect.Lists;
import java.lang.ref.WeakReference;
@@ -36,8 +34,8 @@ import javax.inject.Inject;
/**
* {@link PackageObserver} allows for monitoring the system for changes relating to a particular
- * package. This can be used by {@link CommunalSource} clients to detect when a related package
- * has changed and reloading is necessary.
+ * package. This can be used by clients to detect when a related package has changed and reloading
+ * is necessary.
*/
public class PackageObserver implements Observer {
private static final String TAG = "PackageObserver";
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index c3c3f90539fd..60567c49bfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -51,6 +51,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
@@ -116,6 +117,7 @@ public final class WMShell extends CoreStartable
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
+ private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
@@ -129,7 +131,7 @@ public final class WMShell extends CoreStartable
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
- private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
+ private KeyguardStateController.Callback mCompatUIKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
@@ -143,6 +145,7 @@ public final class WMShell extends CoreStartable
Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
+ KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
@@ -154,6 +157,7 @@ public final class WMShell extends CoreStartable
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
+ mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mNavigationModeController = navigationModeController;
mScreenLifecycle = screenLifecycle;
@@ -362,13 +366,13 @@ public final class WMShell extends CoreStartable
@VisibleForTesting
void initCompatUi(CompatUI sizeCompatUI) {
- mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ mCompatUIKeyguardCallback = new KeyguardStateController.Callback() {
@Override
- public void onKeyguardOccludedChanged(boolean occluded) {
- sizeCompatUI.onKeyguardOccludedChanged(occluded);
+ public void onKeyguardShowingChanged() {
+ sizeCompatUI.onKeyguardShowingChanged(mKeyguardStateController.isShowing());
}
};
- mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
+ mKeyguardStateController.addCallback(mCompatUIKeyguardCallback);
}
void initDragAndDrop(DragAndDrop dragAndDrop) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 8e1e42a9c9a2..e980eb783d79 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -88,6 +88,6 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
fun onPause_clearsTextField() {
mKeyguardPatternViewController.init()
mKeyguardPatternViewController.onPause()
- verify(mKeyguardMessageAreaController).setMessage("")
+ verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 217092e6e4e5..2fc912288623 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,7 +22,6 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -50,8 +49,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private CommunalStateController mCommunalStateController;
- @Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
ConfigurationController mConfigurationController;
@@ -76,7 +73,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mKeyguardClockSwitchController,
mKeyguardStateController,
mKeyguardUpdateMonitor,
- mCommunalStateController,
mConfigurationController,
mDozeParameters,
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
deleted file mode 100644
index a93a15df5986..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.CommunalStateController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-public class KeyguardVisibilityHelperTest extends SysuiTestCase {
- @Mock
- private CommunalStateController mCommunalStateController;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- com.android.systemui.statusbar.phone.DozeParameters mDozeParameters;
- @Mock
- ScreenOffAnimationController mScreenOffAnimationController;
- @Mock
- ViewPropertyAnimator mViewPropertyAnimator;
- @Mock
- View mTargetView;
-
- private KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mTargetView.animate()).thenReturn(mViewPropertyAnimator);
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mTargetView,
- mCommunalStateController, mKeyguardStateController, mDozeParameters,
- mScreenOffAnimationController, false, false);
- }
-
- @Test
- public void testHideOnCommunal() {
- // Verify view is hidden when communal is visible.
- when(mCommunalStateController.getCommunalViewShowing()).thenReturn(true);
- mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
- false, StatusBarState.KEYGUARD);
- verify(mTargetView).setVisibility(View.GONE);
- verify(mTargetView).setAlpha(1.0f);
-
- // Verify view is shown when communal is not visible.
- when(mCommunalStateController.getCommunalViewShowing()).thenReturn(false);
- mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
- false, StatusBarState.KEYGUARD);
- verify(mTargetView).setVisibility(View.VISIBLE);
- }
-
- @Test
- public void testVisibleOnCommunal() {
- when(mCommunalStateController.getCommunalViewShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
-
- // Verify that helpers constructed with visibility on communal are not hidden when communal
- // is present.
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mTargetView,
- mCommunalStateController, mKeyguardStateController, mDozeParameters,
- mScreenOffAnimationController, false, true);
- mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
- false, StatusBarState.KEYGUARD);
- verify(mTargetView).setVisibility(View.VISIBLE);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
deleted file mode 100644
index 8bd5e793dcdd..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.lang.ref.WeakReference;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalHostViewControllerTest extends SysuiTestCase {
- @Mock
- private CommunalStateController mCommunalStateController;
-
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- @Mock
- private KeyguardStateController mKeyguardStateController;
-
- @Mock
- private StatusBarStateController mStatusBarStateController;
-
- @Mock
- private CommunalHostView mCommunalView;
-
- @Mock
- private DozeParameters mDozeParameters;
-
- @Mock
- private ScreenOffAnimationController mScreenOffAnimationController;
-
- private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-
- private CommunalHostViewController mController;
-
- @Mock
- private CommunalSource mCommunalSource;
-
- @Mock
- private View mChildView;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mCommunalView.isAttachedToWindow()).thenReturn(true);
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-
- mController = new CommunalHostViewController(mFakeExecutor, mCommunalStateController,
- mKeyguardUpdateMonitor, mKeyguardStateController, mDozeParameters,
- mScreenOffAnimationController, mStatusBarStateController, mCommunalView);
- mController.init();
- mFakeExecutor.runAllReady();
-
- Mockito.clearInvocations(mCommunalView);
- }
-
- @Test
- public void testShow() {
- ArgumentCaptor<KeyguardStateController.Callback> callbackCapture =
- ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
-
- // Capture callback value for later use.
- verify(mKeyguardStateController).addCallback(callbackCapture.capture());
-
- // Verify the communal view is shown when the controller is initialized with keyguard
- // showing (see setup).
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.VISIBLE);
-
- // Trigger keyguard off to ensure visibility of communal view is changed accordingly.
- when(mKeyguardStateController.isShowing()).thenReturn(false);
- callbackCapture.getValue().onKeyguardShowingChanged();
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.INVISIBLE);
- }
-
- @Test
- public void testHideOnOcclude() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
- // Capture callback value for later use.
- verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
-
- // Establish a visible communal view.
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.VISIBLE);
- Mockito.clearInvocations(mCommunalView);
-
- // Occlude.
- Mockito.clearInvocations(mCommunalView);
- callbackCapture.getValue().onKeyguardOccludedChanged(true);
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.INVISIBLE);
-
- // Unocclude.
- Mockito.clearInvocations(mCommunalView);
- callbackCapture.getValue().onKeyguardOccludedChanged(false);
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.VISIBLE);
- }
-
- @Test
- public void testReportOcclusion() {
- // Ensure CommunalHostViewController reports view occluded when either the QS or Shade is
- // expanded.
- clearInvocations(mCommunalStateController);
- mController.updateShadeExpansion(0);
- verify(mCommunalStateController).setCommunalViewOccluded(false);
- clearInvocations(mCommunalStateController);
- mController.updateQsExpansion(.5f);
- verify(mCommunalStateController).setCommunalViewOccluded(true);
- clearInvocations(mCommunalStateController);
- mController.updateShadeExpansion(.7f);
- verify(mCommunalStateController).setCommunalViewOccluded(true);
- clearInvocations(mCommunalStateController);
- mController.updateShadeExpansion(0);
- verify(mCommunalStateController).setCommunalViewOccluded(true);
- clearInvocations(mCommunalStateController);
- mController.updateQsExpansion(0f);
- verify(mCommunalStateController).setCommunalViewOccluded(false);
- clearInvocations(mCommunalStateController);
- }
-
- @Test
- public void testCommunalStateControllerHideNotified() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
- // Capture callback value for later use.
- verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
-
- // Establish a visible communal view.
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
-
- // Occlude
- clearInvocations(mCommunalStateController);
- callbackCapture.getValue().onKeyguardOccludedChanged(true);
- mFakeExecutor.runAllReady();
-
- // Verify state controller is notified communal view is hidden.
- verify(mCommunalStateController).setCommunalViewShowing(false);
- }
-
- @Test
- public void testAlphaPropagation() {
- final float alpha = 0.8f;
-
- // Ensure alpha setting is propagated to children.
- when(mCommunalView.getChildCount()).thenReturn(1);
- when(mCommunalView.getChildAt(0)).thenReturn(mChildView);
- mController.setAlpha(alpha);
- verify(mChildView).setAlpha(alpha);
- verify(mCommunalView).setAlpha(alpha);
- }
-
- @Test
- public void testMultipleShowRequestSuppression() {
- // Ensure first request invokes source.
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- verify(mCommunalSource).requestCommunalView(any());
- clearInvocations(mCommunalSource);
-
- // Ensure subsequent identical request is suppressed
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- verify(mCommunalSource, never()).requestCommunalView(any());
- }
-
- @Test
- public void testNoShowInvocationOnBouncer() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
- // Capture callback value for later use.
- verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
-
- // Set source so it will be cleared if in invalid state.
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- clearInvocations(mCommunalStateController, mCommunalView);
-
- // Change bouncer to showing.
- callbackCapture.getValue().onKeyguardBouncerChanged(true);
- mFakeExecutor.runAllReady();
-
- // Verify that there were no requests to remove all child views or set the communal
- // state to not showing.
- verify(mCommunalStateController, never()).setCommunalViewShowing(eq(false));
- verify(mCommunalView, never()).removeAllViews();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
deleted file mode 100644
index 0a0266bc1955..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.CommunalHostViewPositionAlgorithm.Result;
-
-import org.junit.Test;
-
-
-@SmallTest
-public class CommunalHostViewPositionAlgorithmTest extends SysuiTestCase {
- @Test
- public void testOutput() {
- final float expansion = 0.25f;
- final int height = 120;
-
- final CommunalHostViewPositionAlgorithm algorithm = new CommunalHostViewPositionAlgorithm();
- algorithm.setup(expansion, height);
- final Result result = new Result();
- algorithm.run(result);
-
- // Verify the communal view is shifted offscreen vertically by the correct amount.
- assertThat((1 - expansion) * -height).isEqualTo(result.communalY);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
deleted file mode 100644
index c5b1a1d8bac5..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalSettingCondition;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalSettingConditionTest extends SysuiTestCase {
- private FakeSettings mSecureSettings;
- private CommunalSettingCondition mCondition;
-
- @Before
- public void setup() {
- final FakeHandler handler = new FakeHandler(Looper.getMainLooper());
- mSecureSettings = new FakeSettings();
- mCondition = new CommunalSettingCondition(handler, mSecureSettings);
- }
-
- @Test
- public void addCallback_communalSettingEnabled_immediatelyReportsTrue() {
- updateCommunalSetting(true);
-
- final Condition.Callback callback = mock(Condition.Callback.class);
- mCondition.addCallback(callback);
- verify(callback).onConditionChanged(mCondition);
- assertThat(mCondition.isConditionMet()).isTrue();
- }
-
- @Test
- public void addCallback_communalSettingDisabled_noReport() {
- updateCommunalSetting(false);
-
- final Condition.Callback callback = mock(Condition.Callback.class);
- mCondition.addCallback(callback);
- verify(callback, never()).onConditionChanged(eq(mCondition));
- }
-
- @Test
- public void updateCallback_communalSettingEnabled_reportsTrue() {
- updateCommunalSetting(false);
-
- final Condition.Callback callback = mock(Condition.Callback.class);
- mCondition.addCallback(callback);
- clearInvocations(callback);
-
- updateCommunalSetting(true);
- verify(callback).onConditionChanged(mCondition);
- assertThat(mCondition.isConditionMet()).isTrue();
- }
-
- @Test
- public void updateCallback_communalSettingDisabled_reportsFalse() {
- updateCommunalSetting(true);
-
- final Condition.Callback callback = mock(Condition.Callback.class);
- mCondition.addCallback(callback);
- clearInvocations(callback);
-
- updateCommunalSetting(false);
- verify(callback).onConditionChanged(mCondition);
- assertThat(mCondition.isConditionMet()).isFalse();
- }
-
- @Test
- public void updateCallback_communalSettingDidNotChange_neverReportDup() {
- updateCommunalSetting(true);
-
- final Condition.Callback callback = mock(Condition.Callback.class);
- mCondition.addCallback(callback);
- clearInvocations(callback);
-
- updateCommunalSetting(true);
- verify(callback, never()).onConditionChanged(mCondition);
- assertThat(mCondition.isConditionMet()).isTrue();
- }
-
- private void updateCommunalSetting(boolean value) {
- mSecureSettings.putIntForUser(Settings.Secure.COMMUNAL_MODE_ENABLED, value ? 1 : 0,
- UserHandle.USER_SYSTEM);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
deleted file mode 100644
index df1cc766d162..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.condition.Monitor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.lang.ref.WeakReference;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class CommunalSourceMonitorTest extends SysuiTestCase {
- @Mock private Monitor mCommunalConditionsMonitor;
-
- @Captor private ArgumentCaptor<Monitor.Callback> mConditionsCallbackCaptor;
-
- private CommunalSourceMonitor mCommunalSourceMonitor;
- private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mCommunalSourceMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
- }
-
- @Test
- public void testSourceAddedBeforeCallbackAdded() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- setSource(source);
- mCommunalSourceMonitor.addCallback(callback);
- setConditionsMet(true);
-
- verifyOnSourceAvailableCalledWith(callback, source);
- }
-
- @Test
- public void testRemoveCallback() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- mCommunalSourceMonitor.addCallback(callback);
- mCommunalSourceMonitor.removeCallback(callback);
- setSource(source);
-
- verify(callback, never()).onSourceAvailable(any());
- }
-
- @Test
- public void testAddCallbackWithConditionsMet() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- mCommunalSourceMonitor.addCallback(callback);
- setConditionsMet(true);
- clearInvocations(callback);
- setSource(source);
-
- verifyOnSourceAvailableCalledWith(callback, source);
- }
-
- @Test
- public void testAddCallbackWithConditionsNotMet() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- mCommunalSourceMonitor.addCallback(callback);
- setConditionsMet(false);
- setSource(source);
-
- verify(callback, never()).onSourceAvailable(any());
- }
-
- @Test
- public void testConditionsAreMetAfterCallbackAdded() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- mCommunalSourceMonitor.addCallback(callback);
- setSource(source);
-
- // The callback should not have executed since communal is disabled.
- verify(callback, never()).onSourceAvailable(any());
-
- // The callback should execute when all conditions are met.
- setConditionsMet(true);
- verifyOnSourceAvailableCalledWith(callback, source);
- }
-
- @Test
- public void testConditionsNoLongerMetAfterCallbackAdded() {
- final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
- final CommunalSource source = mock(CommunalSource.class);
-
- mCommunalSourceMonitor.addCallback(callback);
- setSource(source);
- setConditionsMet(true);
- verifyOnSourceAvailableCalledWith(callback, source);
-
- // The callback should execute again when conditions are no longer met, with a value of
- // null.
- setConditionsMet(false);
- verify(callback).onSourceAvailable(null);
- }
-
- private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback,
- CommunalSource source) {
- final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
- ArgumentCaptor.forClass(WeakReference.class);
- verify(callback).onSourceAvailable(sourceCapture.capture());
- assertThat(sourceCapture.getValue().get()).isEqualTo(source);
- }
-
- // Pushes an update on whether the communal conditions are met, assuming that a callback has
- // been registered with the communal conditions monitor.
- private void setConditionsMet(boolean value) {
- mExecutor.runAllReady();
- verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
- final Monitor.Callback conditionsCallback =
- mConditionsCallbackCaptor.getValue();
- conditionsCallback.onConditionsChanged(value);
- mExecutor.runAllReady();
- }
-
- private void setSource(CommunalSource source) {
- mCommunalSourceMonitor.setSource(source);
- mExecutor.runAllReady();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
deleted file mode 100644
index 1d9a0594d7e2..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;;
-import com.android.systemui.util.ref.GcWeakReference;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalSourcePrimerTest extends SysuiTestCase {
- private static final String TEST_COMPONENT_NAME = "com.google.tests/.CommunalService";
- private static final int MAX_RETRIES = 5;
- private static final int RETRY_DELAY_MS = 1000;
- private static final int CONNECTION_MIN_DURATION_MS = 5000;
-
- // A simple implementation of {@link CommunalSource.Observer} to capture a callback value.
- // Used to ensure the references to a {@link CommunalSource.Observer.Callback} can be fully
- // removed.
- private static class FakeObserver implements CommunalSource.Observer {
- public GcWeakReference<Callback> mLastCallback;
-
- @Override
- public void addCallback(Callback callback) {
- mLastCallback = new GcWeakReference<>(callback);
- }
-
- @Override
- public void removeCallback(Callback callback) {
- if (mLastCallback.get() == callback) {
- mLastCallback = null;
- }
- }
- }
-
- // A simple implementation of {@link CommunalSource} to capture callback values. This
- // implementation better emulates the {@link WeakReference} wrapping behavior of
- // {@link CommunalSource} implementations than a mock.
- private static class FakeSource implements CommunalSource {
- @Override
- public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
- return null;
- }
- }
-
- @Mock
- private Context mContext;
-
- @Mock
- private Resources mResources;
-
- private FakeSystemClock mFakeClock = new FakeSystemClock();
- private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
-
- private FakeSource mSource = new FakeSource();
-
- @Mock
- private CommunalSourceMonitor mCommunalSourceMonitor;
-
- @Mock
- private CommunalSource.Connector mConnector;
-
- @Mock
- private CommunalSource.Connection mConnection;
-
- private FakeObserver mObserver = new FakeObserver();
-
- private CommunalSourcePrimer mPrimer;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mResources.getInteger(R.integer.config_communalSourceMaxReconnectAttempts))
- .thenReturn(MAX_RETRIES);
- when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
- .thenReturn(RETRY_DELAY_MS);
- when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
- .thenReturn(RETRY_DELAY_MS);
- when(mResources.getString(R.string.config_communalSourceComponent))
- .thenReturn(TEST_COMPONENT_NAME);
- when(mResources.getInteger(R.integer.config_connectionMinDuration))
- .thenReturn(CONNECTION_MIN_DURATION_MS);
-
- mPrimer = new CommunalSourcePrimer(mContext, mResources, mFakeClock, mFakeExecutor,
- mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
- }
-
- private CommunalSource.Connection.Callback captureCallbackAndSend(
- CommunalSource.Connector connector, Optional<CommunalSource> source) {
- ArgumentCaptor<CommunalSource.Connection.Callback> connectionCallback =
- ArgumentCaptor.forClass(CommunalSource.Connection.Callback.class);
-
- verify(connector).connect(connectionCallback.capture());
- Mockito.clearInvocations(connector);
-
- final CommunalSource.Connection.Callback callback = connectionCallback.getValue();
- callback.onSourceEstablished(source);
-
- return callback;
- }
-
- @Test
- public void testConnect() {
- mPrimer.onBootCompleted();
- captureCallbackAndSend(mConnector, Optional.of(mSource));
- verify(mCommunalSourceMonitor).setSource(mSource);
- }
-
- @Test
- public void testRetryOnBindFailure() throws Exception {
- mPrimer.onBootCompleted();
-
- // Verify attempts happen. Note that we account for the retries plus initial attempt, which
- // is not scheduled.
- for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
- captureCallbackAndSend(mConnector, Optional.empty());
- mFakeExecutor.advanceClockToNext();
- mFakeExecutor.runAllReady();
- }
-
- verify(mCommunalSourceMonitor, never()).setSource(Mockito.notNull());
- }
-
- @Test
- public void testRetryOnDisconnectFailure() throws Exception {
- mPrimer.onBootCompleted();
- // Verify attempts happen. Note that we account for the retries plus initial attempt, which
- // is not scheduled.
- for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
- final CommunalSource.Connection.Callback callback =
- captureCallbackAndSend(mConnector, Optional.of(mSource));
- verify(mCommunalSourceMonitor).setSource(Mockito.notNull());
- clearInvocations(mCommunalSourceMonitor);
- callback.onDisconnected();
- mFakeExecutor.advanceClockToNext();
- mFakeExecutor.runAllReady();
- }
-
- verify(mConnector, never()).connect(any());
- }
-
- @Test
- public void testAttemptOnPackageChange() {
- mPrimer.onBootCompleted();
- captureCallbackAndSend(mConnector, Optional.empty());
-
- mObserver.mLastCallback.get().onSourceChanged();
-
- verify(mConnector, times(1)).connect(any());
- }
-
- @Test
- public void testDisconnect() {
- mPrimer.onBootCompleted();
- final CommunalSource.Connection.Callback callback =
- captureCallbackAndSend(mConnector, Optional.of(mSource));
- verify(mCommunalSourceMonitor).setSource(mSource);
-
- mFakeClock.advanceTime(CONNECTION_MIN_DURATION_MS + 1);
- callback.onDisconnected();
-
- verify(mConnector).connect(any());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
deleted file mode 100644
index 7f85c35af742..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalStateControllerTest extends SysuiTestCase {
- @Mock
- private CommunalStateController.Callback mCallback;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testDefaultCommunalViewShowingState() {
- // The state controller should report the communal view as not showing by default.
- final CommunalStateController stateController = new CommunalStateController();
- assertThat(stateController.getCommunalViewShowing()).isFalse();
- }
-
- @Test
- public void testNotifyCommunalSurfaceShow() {
- final CommunalStateController stateController = new CommunalStateController();
- stateController.addCallback(mCallback);
-
- // Verify setting communal view to showing propagates to callback.
- stateController.setCommunalViewShowing(true);
- verify(mCallback).onCommunalViewShowingChanged();
- assertThat(stateController.getCommunalViewShowing()).isTrue();
-
- clearInvocations(mCallback);
-
- // Verify setting communal view to not showing propagates to callback.
- stateController.setCommunalViewShowing(false);
- verify(mCallback).onCommunalViewShowingChanged();
- assertThat(stateController.getCommunalViewShowing()).isFalse();
- }
-
- @Test
- public void testCallbackRegistration() {
- final CommunalStateController stateController = new CommunalStateController();
- stateController.addCallback(mCallback);
-
- // Verify setting communal view to showing propagates to callback.
- stateController.setCommunalViewShowing(true);
- verify(mCallback).onCommunalViewShowingChanged();
-
- clearInvocations(mCallback);
-
- stateController.removeCallback(mCallback);
- clearInvocations(mCallback);
-
- // Verify callback not invoked after removing from state controller.
- stateController.setCommunalViewShowing(false);
- verify(mCallback, never()).onCommunalViewShowingChanged();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/PackageObserverTest.java
deleted file mode 100644
index 1cd60698353b..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/PackageObserverTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class PackageObserverTest extends SysuiTestCase {
- private static final String PACKAGE_NAME = "com.foo.bar";
-
- @Mock
- Context mContext;
-
- @Mock
- CommunalSource.Observer.Callback mCallback;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testChange() {
- final PackageObserver observer = new PackageObserver(mContext, PACKAGE_NAME);
- final ArgumentCaptor<BroadcastReceiver> receiverCapture =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
-
- observer.addCallback(mCallback);
-
- // Verify broadcast receiver registered.
- verify(mContext).registerReceiver(receiverCapture.capture(), any(), anyInt());
-
- // Simulate package change.
- receiverCapture.getValue().onReceive(mContext, new Intent());
-
- // Check that callback was informed.
- verify(mCallback).onSourceChanged();
-
- observer.removeCallback(mCallback);
-
- // Make sure receiver is unregistered on last callback removal
- verify(mContext).unregisterReceiver(receiverCapture.getValue());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 35fda1392512..7d7ccb462b3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.dreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -222,4 +223,25 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
verify(mStateController).setOverlayActive(false);
}
+
+ @Test
+ public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
+ when(mDreamOverlayContainerView.getParent())
+ .thenReturn(mDreamOverlayContainerViewParent)
+ .thenReturn(null);
+
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+
+ // Destroy the service.
+ mService.onDestroy();
+
+ // Run executor tasks.
+ mMainExecutor.runAllReady();
+
+ verify(mWindowManager, never()).addView(any(), any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
index 3b17a8071cb2..ed1cf69ad8c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
@@ -15,26 +15,31 @@
*/
package com.android.systemui.dreams;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.smartspace.SmartspaceTarget;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+import com.android.systemui.dreams.smartspace.DreamsSmartspaceController;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class SmartSpaceComplicationTest extends SysuiTestCase {
@@ -42,7 +47,7 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
private Context mContext;
@Mock
- private LockscreenSmartspaceController mSmartspaceController;
+ private DreamsSmartspaceController mSmartspaceController;
@Mock
private DreamOverlayStateController mDreamOverlayStateController;
@@ -60,7 +65,6 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
*/
@Test
public void testAvailability() {
- when(mSmartspaceController.isEnabled()).thenReturn(false);
final SmartSpaceComplication.Registrant registrant = new SmartSpaceComplication.Registrant(
mContext,
@@ -68,10 +72,22 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
mComplication,
mSmartspaceController);
registrant.start();
- verify(mDreamOverlayStateController, never()).addComplication(any());
+ verify(mDreamOverlayStateController, never()).addComplication(eq(mComplication));
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- registrant.start();
+
+ final ArgumentCaptor<DreamOverlayStateController.Callback> dreamCallbackCaptor =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(dreamCallbackCaptor.capture());
+
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ dreamCallbackCaptor.getValue().onStateChanged();
+
+ final ArgumentCaptor<BcSmartspaceDataPlugin.SmartspaceTargetListener> listenerCaptor =
+ ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
+ verify(mSmartspaceController).addListener(listenerCaptor.capture());
+
+ final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 6fead9efa767..f00fbe6ac4d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -341,6 +341,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
public void testCreateActionItems_lockdownEnabled_doesShowLockdown() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
String[] actions = {
@@ -365,8 +366,10 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
public void testCreateActionItems_lockdownDisabled_doesNotShowLockdown() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
// make sure lockdown action will NOT be shown
doReturn(false).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
// lockdown action not allowed
@@ -386,6 +389,32 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
}
@Test
+ public void testCreateActionItems_emergencyDisabled_doesNotShowEmergency() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ // make sure emergency action will NOT be shown
+ doReturn(false).when(mGlobalActionsDialogLite).shouldDisplayEmergency();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ String[] actions = {
+ // emergency action not allowed
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ mGlobalActionsDialogLite.createActionItems();
+
+ assertItemsOfType(mGlobalActionsDialogLite.mItems,
+ GlobalActionsDialogLite.LockDownAction.class,
+ GlobalActionsDialogLite.ShutDownAction.class,
+ GlobalActionsDialogLite.RestartAction.class);
+ assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
+ assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
+ }
+
+ @Test
public void testShouldLogLockdownPress() {
GlobalActionsDialogLite.LockDownAction lockDownAction =
mGlobalActionsDialogLite.new LockDownAction();
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 2be30b39763e..13e582196ffb 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
@@ -42,6 +42,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -50,6 +51,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -66,6 +69,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+ NearbyMediaDevicesManager.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
@@ -80,7 +85,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 789822e262d5..6230700a6a2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -33,9 +33,11 @@ import android.content.Context;
import android.graphics.drawable.Icon;
import android.media.MediaDescription;
import android.media.MediaMetadata;
+import android.media.NearbyDevice;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.text.TextUtils;
@@ -51,17 +53,21 @@ import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.google.common.collect.ImmutableList;
+
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
@RunWith(AndroidTestingRunner.class)
@@ -86,6 +92,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class);
private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
+ private NearbyDevice mNearbyDevice1 = mock(NearbyDevice.class);
+ private NearbyDevice mNearbyDevice2 = mock(NearbyDevice.class);
private MediaMetadata mMediaMetadata = mock(MediaMetadata.class);
private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
private ShadeController mShadeController = mock(ShadeController.class);
@@ -93,12 +101,15 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+ NearbyMediaDevicesManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
private LocalMediaManager mLocalMediaManager;
private List<MediaController> mMediaControllers = new ArrayList<>();
private List<MediaDevice> mMediaDevices = new ArrayList<>();
+ private List<NearbyDevice> mNearbyDevices = new ArrayList<>();
private MediaDescription mMediaDescription;
private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
@@ -115,7 +126,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -127,6 +139,13 @@ public class MediaOutputControllerTest extends SysuiTestCase {
when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
mMediaDevices.add(mMediaDevice1);
mMediaDevices.add(mMediaDevice2);
+
+ when(mNearbyDevice1.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID);
+ when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_CLOSE);
+ when(mNearbyDevice2.getMediaRoute2Id()).thenReturn(TEST_DEVICE_2_ID);
+ when(mNearbyDevice2.getRangeZone()).thenReturn(NearbyDevice.RANGE_FAR);
+ mNearbyDevices.add(mNearbyDevice1);
+ mNearbyDevices.add(mNearbyDevice2);
}
@Test
@@ -159,7 +178,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mMediaOutputController.start(mCb);
@@ -167,6 +187,13 @@ public class MediaOutputControllerTest extends SysuiTestCase {
}
@Test
+ public void start_nearbyMediaDevicesManagerNotNull_registersNearbyDevicesCallback() {
+ mMediaOutputController.start(mCb);
+
+ verify(mNearbyMediaDevicesManager).registerNearbyDevicesCallback(any());
+ }
+
+ @Test
public void stop_withPackageName_verifyMediaControllerDeinit() {
mMediaOutputController.start(mCb);
reset(mMediaController);
@@ -180,7 +207,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mMediaOutputController.start(mCb);
@@ -189,6 +217,39 @@ public class MediaOutputControllerTest extends SysuiTestCase {
verify(mMediaController, never()).unregisterCallback(any());
}
+
+ @Test
+ public void stop_nearbyMediaDevicesManagerNotNull_unregistersNearbyDevicesCallback() {
+ mMediaOutputController.start(mCb);
+ reset(mMediaController);
+
+ mMediaOutputController.stop();
+
+ verify(mNearbyMediaDevicesManager).unregisterNearbyDevicesCallback(any());
+ }
+
+ @Test
+ public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
+ mMediaOutputController.start(mCb);
+
+ mMediaOutputController.onDevicesUpdated(ImmutableList.of());
+
+ verify(mNearbyMediaDevicesManager).unregisterNearbyDevicesCallback(any());
+ }
+
+ @Test
+ public void onDeviceListUpdate_withNearbyDevices_updatesRangeInformation()
+ throws RemoteException {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDevicesUpdated(mNearbyDevices);
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ verify(mMediaDevice1).setRangeZone(NearbyDevice.RANGE_CLOSE);
+ verify(mMediaDevice2).setRangeZone(NearbyDevice.RANGE_FAR);
+ }
+
@Test
public void onDeviceListUpdate_verifyDeviceListCallback() {
mMediaOutputController.start(mCb);
@@ -451,7 +512,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
+ mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
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 8a3ea562269d..cb52e7c20464 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
@@ -37,6 +37,7 @@ import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -48,6 +49,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -67,6 +69,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+ NearbyMediaDevicesManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -76,7 +80,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
mMediaOutputController, mUiEventLogger);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index e8cd6c88956d..f186f57fd0e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -35,6 +35,7 @@ import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -46,6 +47,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -66,6 +68,8 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+ NearbyMediaDevicesManager.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -75,7 +79,8 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager));
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 794bc09715af..2a130535c657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -21,13 +21,8 @@ import android.content.Context
import android.media.MediaRoute2Info
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
-import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
-import com.android.systemui.media.taptotransfer.sender.TransferFailed
-import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
-import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
-import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
-import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
+import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
+import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.FakeExecutor
@@ -88,7 +83,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_almostCloseToStartCast_serviceCallbackCalled() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(AlmostCloseToStartCast::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
)
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -103,7 +98,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_almostCloseToEndCast_serviceCallbackCalled() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(AlmostCloseToEndCast::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.name)
)
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -118,7 +113,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(TransferToReceiverTriggered::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.name)
)
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -133,7 +128,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(TransferToThisDeviceTriggered::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
)
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
@@ -146,7 +141,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(TransferToReceiverSucceeded::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.name)
)
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -161,7 +156,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(
- pw, getSenderCommand(TransferToThisDeviceSucceeded::class.simpleName!!)
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.name)
)
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -174,8 +169,10 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
}
@Test
- fun sender_transferFailed_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getSenderCommand(TransferFailed::class.simpleName!!))
+ fun sender_transferToReceiverFailed_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.name)
+ )
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED),
@@ -185,8 +182,23 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
}
@Test
+ fun sender_transferToThisDeviceFailed_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_FAILED.name)
+ )
+
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED),
+ any(),
+ nullable(),
+ nullable())
+ }
+
+ @Test
fun sender_farFromReceiver_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getSenderCommand(FAR_FROM_RECEIVER_STATE))
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(ChipStateSender.FAR_FROM_RECEIVER.name)
+ )
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER),
@@ -197,7 +209,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun receiver_closeToSender_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE))
+ commandRegistry.onShellCommand(
+ pw, getReceiverCommand(ChipStateReceiver.CLOSE_TO_SENDER.name)
+ )
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
@@ -209,7 +223,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test
fun receiver_farFromSender_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE))
+ commandRegistry.onShellCommand(
+ pw, getReceiverCommand(ChipStateReceiver.FAR_FROM_SENDER.name)
+ )
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index adb59eca1e08..ccce5778c150 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -17,7 +17,10 @@
package com.android.systemui.media.taptotransfer.common
import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.os.PowerManager
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -40,6 +43,7 @@ import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
+import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -48,12 +52,16 @@ import org.mockito.MockitoAnnotations
@SmallTest
class MediaTttChipControllerCommonTest : SysuiTestCase() {
- private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
+ private lateinit var controllerCommon: MediaTttChipControllerCommon<ChipInfo>
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
- private lateinit var appIconDrawable: Drawable
+ private lateinit var appIconFromPackageName: Drawable
+ @Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var applicationInfo: ApplicationInfo
@Mock
private lateinit var logger: MediaTttLogger
@Mock
@@ -62,25 +70,36 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var viewUtil: ViewUtil
@Mock
private lateinit var tapGestureDetector: TapGestureDetector
+ @Mock
+ private lateinit var powerManager: PowerManager
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+
+ appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
+ whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
+ whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+ whenever(packageManager.getApplicationInfo(
+ eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+ )).thenReturn(applicationInfo)
+ context.setMockPackageManager(packageManager)
+
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
- context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector
+ context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector, powerManager
)
}
@Test
- fun displayChip_chipAddedAndGestureDetectionStarted() {
+ fun displayChip_chipAddedAndGestureDetectionStartedAndScreenOn() {
controllerCommon.displayChip(getState())
verify(windowManager).addView(any(), any())
verify(tapGestureDetector).addOnGestureDetectedCallback(any(), any())
+ verify(powerManager).wakeUp(any(), any(), any())
}
@Test
@@ -100,7 +119,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(state)
reset(windowManager)
- fakeClock.advanceTime(state.getTimeoutMs() - 1)
+ fakeClock.advanceTime(TIMEOUT_MS - 1)
verify(windowManager, never()).removeView(any())
}
@@ -111,7 +130,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(state)
reset(windowManager)
- fakeClock.advanceTime(state.getTimeoutMs() + 1)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
}
@@ -128,7 +147,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(getState())
// Wait until the timeout for the first display would've happened
- fakeClock.advanceTime(state.getTimeoutMs() - waitTime + 1)
+ fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1)
// Verify we didn't hide the chip
verify(windowManager, never()).removeView(any())
@@ -145,7 +164,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(getState())
// Ensure we still hide the chip eventually
- fakeClock.advanceTime(state.getTimeoutMs() + 1)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
}
@@ -172,16 +191,45 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
}
@Test
- fun setIcon_viewHasIconAndContentDescription() {
+ fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
+ controllerCommon.displayChip(getState())
+ val chipView = getChipView()
+
+ controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
+
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
+ }
+
+ @Test
+ fun displayChip_hasAppIconDrawable_iconIsDrawable() {
controllerCommon.displayChip(getState())
val chipView = getChipView()
- val state = TestChipState(PACKAGE_NAME)
- controllerCommon.setIcon(state, chipView)
+ val drawable = context.getDrawable(R.drawable.ic_alarm)!!
+ controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
- assertThat(chipView.getAppIconView().contentDescription)
- .isEqualTo(state.getAppName(context))
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ }
+
+ @Test
+ fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
+ controllerCommon.displayChip(getState())
+ val chipView = getChipView()
+
+ controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null)
+
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ }
+
+ @Test
+ fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
+ controllerCommon.displayChip(getState())
+ val chipView = getChipView()
+
+ val appName = "Override App Name"
+ controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName)
+
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName)
}
@Test
@@ -218,7 +266,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
}
- private fun getState() = MediaTttChipState(PACKAGE_NAME)
+ private fun getState() = ChipInfo()
private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -235,22 +283,27 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
- ) : MediaTttChipControllerCommon<MediaTttChipState>(
+ powerManager: PowerManager
+ ) : MediaTttChipControllerCommon<ChipInfo>(
context,
logger,
windowManager,
viewUtil,
mainExecutor,
tapGestureDetector,
+ powerManager,
R.layout.media_ttt_chip
) {
- override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
+ override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {
+
}
}
- inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) {
- override fun getAppIcon(context: Context) = appIconDrawable
+ inner class ChipInfo : ChipInfoCommon {
+ override fun getTimeoutMs() = TIMEOUT_MS
}
}
private const val PACKAGE_NAME = "com.android.systemui"
+private const val APP_NAME = "Fake App Name"
+private const val TIMEOUT_MS = 10000L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 56f45896436c..355d3fe4b8d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.os.Handler
+import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -29,6 +30,7 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
@@ -63,6 +65,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var powerManager: PowerManager
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -70,6 +74,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var receiverUiEventLogger: MediaTttReceiverUiEventLogger
@Before
fun setUp() {
@@ -83,6 +89,9 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
+ uiEventLoggerFake = UiEventLoggerFake()
+ receiverUiEventLogger = MediaTttReceiverUiEventLogger(uiEventLoggerFake)
+
controllerReceiver = MediaTttChipControllerReceiver(
commandQueue,
context,
@@ -91,7 +100,9 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
viewUtil,
FakeExecutor(FakeSystemClock()),
TapGestureDetector(context),
+ powerManager,
Handler.getMain(),
+ receiverUiEventLogger,
)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -110,6 +121,9 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER.id
+ )
}
@Test
@@ -122,6 +136,9 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
)
verify(windowManager, never()).addView(any(), any())
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER.id
+ )
}
@Test
@@ -157,44 +174,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
verify(logger).logStateChange(any(), any())
}
- @Test
- fun displayChip_nullAppIconDrawable_iconIsFromPackageName() {
- val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, "appName")
-
- controllerReceiver.displayChip(state)
-
- assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
- }
-
- @Test
- fun displayChip_hasAppIconDrawable_iconIsDrawable() {
- val drawable = context.getDrawable(R.drawable.ic_alarm)!!
- val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
-
- controllerReceiver.displayChip(state)
-
- assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
- }
-
- @Test
- fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
- val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName = null)
-
- controllerReceiver.displayChip(state)
-
- assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
- }
-
- @Test
- fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
- val appName = "Override App Name"
- val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
-
- controllerReceiver.displayChip(state)
-
- assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
- }
-
private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt
new file mode 100644
index 000000000000..ee10ddc521f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.media.taptotransfer.receiver
+
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class MediaTttReceiverUiEventLoggerTest : SysuiTestCase() {
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var logger: MediaTttReceiverUiEventLogger
+
+ @Before
+ fun setUp() {
+ uiEventLoggerFake = UiEventLoggerFake()
+ logger = MediaTttReceiverUiEventLogger(uiEventLoggerFake)
+ }
+
+ @Test
+ fun logReceiverStateChange_eventAssociatedWithStateIsLogged() {
+ val state = ChipStateReceiver.CLOSE_TO_SENDER
+
+ logger.logReceiverStateChange(state)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(state.uiEvent.id)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index fd1d76a5d02d..ef5315428a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
+import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -29,6 +30,7 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -65,6 +67,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var powerManager: PowerManager
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -74,6 +78,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var senderUiEventLogger: MediaTttSenderUiEventLogger
@Before
fun setUp() {
@@ -89,6 +95,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ uiEventLoggerFake = UiEventLoggerFake()
+ senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
controllerSender = MediaTttChipControllerSender(
commandQueue,
@@ -97,7 +105,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
windowManager,
viewUtil,
fakeExecutor,
- TapGestureDetector(context)
+ TapGestureDetector(context),
+ powerManager,
+ senderUiEventLogger
)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -113,8 +123,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(almostCloseToStartCast().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ almostCloseToStartCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id
+ )
}
@Test
@@ -125,8 +139,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(almostCloseToEndCast().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ almostCloseToEndCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id
+ )
}
@Test
@@ -137,8 +155,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToReceiverTriggered().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id
+ )
}
@Test
@@ -149,8 +171,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id
+ )
}
@Test
@@ -161,8 +187,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToReceiverSucceeded().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToReceiverSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id
+ )
}
@Test
@@ -173,8 +203,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToThisDeviceSucceeded().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToThisDeviceSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id
+ )
}
@Test
@@ -185,8 +219,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferFailed().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToReceiverFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id
+ )
}
@Test
@@ -197,8 +235,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- assertThat(getChipView().getChipText())
- .isEqualTo(transferFailed().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToThisDeviceFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id
+ )
}
@Test
@@ -210,6 +252,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
)
verify(windowManager, never()).addView(any(), any())
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER.id
+ )
}
@Test
@@ -250,7 +295,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -264,7 +311,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -278,7 +327,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -292,7 +343,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -306,7 +359,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@@ -355,8 +410,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
getChipView().getUndoButton().performClick()
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED.id
+ )
}
@Test
@@ -367,7 +426,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@@ -416,19 +477,41 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
getChipView().getUndoButton().performClick()
- assertThat(getChipView().getChipText())
- .isEqualTo(transferToReceiverTriggered().getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED.id
+ )
+ }
+
+ @Test
+ fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
+ val state = transferToReceiverFailed()
+ controllerSender.displayChip(state)
+
+ val chipView = getChipView()
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ assertThat(getChipView().getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
- fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
- val state = transferFailed()
+ fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
+ val state = transferToThisDeviceFailed()
controllerSender.displayChip(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(getChipView().getChipText()).isEqualTo(
+ state.state.getChipTextString(context, OTHER_DEVICE_NAME)
+ )
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
@@ -475,7 +558,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferFailed())
+ controllerSender.displayChip(transferToReceiverFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@@ -498,7 +581,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason")
- fakeClock.advanceTime(state.getTimeoutMs() + 1)
+ fakeClock.advanceTime(state.state.timeout + 1)
verify(windowManager).removeView(any())
}
@@ -521,7 +604,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason")
- fakeClock.advanceTime(state.getTimeoutMs() + 1)
+ fakeClock.advanceTime(state.state.timeout + 1)
verify(windowManager).removeView(any())
}
@@ -546,34 +629,35 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToStartCast() =
- AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
+ ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToEndCast() =
- AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
+ ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverTriggered() =
- TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME)
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceTriggered() =
- TransferToThisDeviceTriggered(PACKAGE_NAME)
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
- TransferToReceiverSucceeded(
- PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
- )
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED, routeInfo, undoCallback)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
- TransferToThisDeviceSucceeded(
- PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
- )
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED, routeInfo, undoCallback)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToReceiverFailed() =
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
- private fun transferFailed() = TransferFailed(PACKAGE_NAME)
+ private fun transferToThisDeviceFailed() =
+ ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
}
private const val APP_NAME = "Fake app name"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
new file mode 100644
index 000000000000..263637a6b6e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
@@ -0,0 +1,46 @@
+package com.android.systemui.media.taptotransfer.sender
+
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class MediaTttSenderUiEventLoggerTest : SysuiTestCase() {
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var logger: MediaTttSenderUiEventLogger
+
+ @Before
+ fun setUp () {
+ uiEventLoggerFake = UiEventLoggerFake()
+ logger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
+ }
+
+ @Test
+ fun logSenderStateChange_eventAssociatedWithStateIsLogged() {
+ val state = ChipStateSender.ALMOST_CLOSE_TO_END_CAST
+ logger.logSenderStateChange(state)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(state.uiEvent.id)
+ }
+
+ @Test
+ fun logUndoClicked_undoEventLogged() {
+ val undoEvent = MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
+
+ logger.logUndoClicked(undoEvent)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(undoEvent.id)
+ }
+
+ @Test
+ fun logUndoClicked_notUndoEvent_eventNotLogged() {
+ logger.logUndoClicked(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
new file mode 100644
index 000000000000..57803e874f93
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.smartspace
+
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dreams.smartspace.DreamsSmartspaceController
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Optional
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DreamSmartspaceControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var smartspaceManager: SmartspaceManager
+
+ @Mock
+ private lateinit var execution: Execution
+
+ @Mock
+ private lateinit var uiExecutor: Executor
+
+ @Mock
+ private lateinit var viewComponentFactory: SmartspaceViewComponent.Factory
+
+ @Mock
+ private lateinit var viewComponent: SmartspaceViewComponent
+
+ @Mock
+ private lateinit var targetFilter: SmartspaceTargetFilter
+
+ @Mock
+ private lateinit var plugin: BcSmartspaceDataPlugin
+
+ @Mock
+ private lateinit var precondition: SmartspacePrecondition
+
+ @Mock
+ private lateinit var smartspaceView: BcSmartspaceDataPlugin.SmartspaceView
+
+ @Mock
+ private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener
+
+ @Mock
+ private lateinit var session: SmartspaceSession
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(viewComponentFactory.create(any(), eq(plugin), any()))
+ .thenReturn(viewComponent)
+ `when`(viewComponent.getView()).thenReturn(smartspaceView)
+ `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
+ }
+
+ /**
+ * Ensures smartspace session begins on a listener only flow.
+ */
+ @Test
+ fun testConnectOnListen() {
+ val controller = DreamsSmartspaceController(context,
+ smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition,
+ Optional.of(targetFilter), Optional.of(plugin))
+
+ `when`(precondition.conditionsMet()).thenReturn(true)
+ controller.addListener(listener)
+
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ var targetListener = withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> {
+ verify(session).addOnTargetsAvailableListener(any(), capture())
+ }
+
+ `when`(targetFilter.filterSmartspaceTarget(any())).thenReturn(true)
+
+ var target = Mockito.mock(SmartspaceTarget::class.java)
+ targetListener.onTargetsAvailable(listOf(target))
+
+ var targets = withArgCaptor<List<SmartspaceTarget>> {
+ verify(plugin).onTargetsAvailable(capture())
+ }
+
+ assertThat(targets.contains(target)).isTrue()
+
+ controller.removeListener(listener)
+
+ verify(session).close()
+ }
+
+ /**
+ * A class which implements SmartspaceView and extends View. This is mocked to provide the right
+ * object inheritance and interface implementation used in DreamSmartspaceController
+ */
+ private class TestView(context: Context?) : View(context),
+ BcSmartspaceDataPlugin.SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {}
+
+ override fun setPrimaryTextColor(color: Int) {}
+
+ override fun setDozeAmount(amount: Float) {}
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {}
+
+ override fun setDnd(image: Drawable?, description: String?) {}
+
+ override fun setNextAlarm(image: Drawable?, description: String?) {}
+
+ override fun setMediaTarget(target: SmartspaceTarget?) {}
+
+ override fun getSelectedPage(): Int { return 0; }
+ }
+
+ /**
+ * Ensures session begins when a view is attached.
+ */
+ @Test
+ fun testConnectOnViewCreate() {
+ val controller = DreamsSmartspaceController(context,
+ smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition,
+ Optional.of(targetFilter),
+ Optional.of(plugin))
+
+ `when`(precondition.conditionsMet()).thenReturn(true)
+ controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java))
+
+ var stateChangeListener = withArgCaptor<View.OnAttachStateChangeListener> {
+ verify(viewComponentFactory).create(any(), eq(plugin), capture())
+ }
+
+ var mockView = Mockito.mock(TestView::class.java)
+ `when`(precondition.conditionsMet()).thenReturn(true)
+ stateChangeListener.onViewAttachedToWindow(mockView)
+
+ verify(smartspaceManager).createSmartspaceSession(any())
+
+ stateChangeListener.onViewDetachedFromWindow(mockView)
+
+ verify(session).close()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
new file mode 100644
index 000000000000..d29e9a66a331
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
@@ -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 com.android.systemui.smartspace
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.concurrency.Execution
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class LockscreenPreconditionTest : SysuiTestCase() {
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+
+ @Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
+
+ @Mock
+ private lateinit var execution: Execution
+
+ @Mock
+ private lateinit var listener: SmartspacePrecondition.Listener
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ /**
+ * Ensures fully enabled state is published.
+ */
+ @Test
+ fun testFullyEnabled() {
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
+ .thenReturn(true)
+ val precondition = LockscreenPrecondition(featureFlags, deviceProvisionedController,
+ execution)
+ precondition.addListener(listener)
+
+ `verify`(listener).onCriteriaChanged()
+ assertThat(precondition.conditionsMet()).isTrue()
+ }
+
+ /**
+ * Ensures fully enabled state is published.
+ */
+ @Test
+ fun testProvisioning() {
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
+ .thenReturn(true)
+ val precondition =
+ LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+ precondition.addListener(listener)
+
+ verify(listener).onCriteriaChanged()
+ assertThat(precondition.conditionsMet()).isFalse()
+
+ var argumentCaptor = ArgumentCaptor.forClass(DeviceProvisionedController
+ .DeviceProvisionedListener::class.java)
+ verify(deviceProvisionedController).addCallback(argumentCaptor.capture())
+
+ Mockito.clearInvocations(listener)
+
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ argumentCaptor.value.onDeviceProvisionedChanged()
+ verify(listener).onCriteriaChanged()
+ assertThat(precondition.conditionsMet()).isTrue()
+ }
+
+ /**
+ * Makes sure user setup changes are propagated.
+ */
+ @Test
+ fun testUserSetup() {
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
+ .thenReturn(true)
+ val precondition =
+ LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+ precondition.addListener(listener)
+
+ verify(listener).onCriteriaChanged()
+ assertThat(precondition.conditionsMet()).isFalse()
+
+ var argumentCaptor = ArgumentCaptor.forClass(DeviceProvisionedController
+ .DeviceProvisionedListener::class.java)
+ verify(deviceProvisionedController).addCallback(argumentCaptor.capture())
+
+ Mockito.clearInvocations(listener)
+
+ `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ argumentCaptor.value.onUserSetupChanged()
+ verify(listener).onCriteriaChanged()
+ assertThat(precondition.conditionsMet()).isTrue()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
new file mode 100644
index 000000000000..185a8384dd75
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.smartspace
+
+import android.app.smartspace.SmartspaceTarget
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class LockscreenTargetFilterTest : SysuiTestCase() {
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+
+ @Mock
+ private lateinit var userTracker: UserTracker
+
+ @Mock
+ private lateinit var execution: Execution
+
+ @Mock
+ private lateinit var handler: Handler
+
+ @Mock
+ private lateinit var contentResolver: ContentResolver
+
+ @Mock
+ private lateinit var uiExecution: Executor
+
+ @Mock
+ private lateinit var userHandle: UserHandle
+
+ @Mock
+ private lateinit var listener: SmartspaceTargetFilter.Listener
+
+ @Mock
+ private lateinit var lockScreenAllowPrivateNotificationsUri: Uri
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(userTracker.userHandle).thenReturn(userHandle)
+ `when`(secureSettings
+ .getUriFor(eq(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS)))
+ .thenReturn(lockScreenAllowPrivateNotificationsUri)
+ }
+
+ /**
+ * Ensures {@link Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS} is
+ * tracked.
+ */
+ @Test
+ fun testLockscreenAllowPrivateNotifications() {
+ var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+ `when`(secureSettings
+ .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(0)
+ var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ contentResolver, uiExecution)
+
+ filter.addListener(listener)
+ var smartspaceTarget = mock(SmartspaceTarget::class.java)
+ `when`(smartspaceTarget.userHandle).thenReturn(userHandle)
+ `when`(smartspaceTarget.isSensitive).thenReturn(true)
+ assertThat(filter.filterSmartspaceTarget(smartspaceTarget)).isFalse()
+
+ var settingCaptor = ArgumentCaptor.forClass(ContentObserver::class.java)
+
+ verify(contentResolver).registerContentObserver(eq(lockScreenAllowPrivateNotificationsUri),
+ anyBoolean(), settingCaptor.capture(), anyInt())
+
+ `when`(secureSettings
+ .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(1)
+
+ clearInvocations(listener)
+ settingCaptor.value.onChange(false, mock(Uri::class.java))
+ verify(listener, atLeast(1)).onCriteriaChanged()
+ assertThat(filter.filterSmartspaceTarget(smartspaceTarget)).isTrue()
+ }
+
+ /**
+ * Ensures user switches are tracked.
+ */
+ @Test
+ fun testUserSwitchCallback() {
+ var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+ `when`(secureSettings
+ .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
+ .thenReturn(0)
+ var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ contentResolver, uiExecution)
+
+ filter.addListener(listener)
+
+ var userTrackerCallback = withArgCaptor<UserTracker.Callback> {
+ verify(userTracker).addCallback(capture(), any())
+ }
+
+ clearInvocations(listener)
+ userTrackerCallback.onUserChanged(0, context)
+
+ verify(listener).onCriteriaChanged()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 25a5b900f15a..067caa98102b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -87,6 +87,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
row = helper.createRow()
context.getOrCreateTestableResources()
.addOverride(R.bool.config_use_split_notification_shade, false)
+ context.getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
transitionController = LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
logger = logger,
@@ -247,6 +249,17 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
}
@Test
+ fun testDragDownAmount_depthDistanceIsZero_doesNotSetProgress() {
+ context.getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
+ configurationController.notifyConfigurationChanged()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(depthController, never()).transitionToFullShadeProgress
+ }
+
+ @Test
fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
transitionController.dragDownAmount = 10f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index 353647b65cc0..4d8949562e08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -80,8 +80,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
Notification.Action firstAction =
createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent).build();
Notification.Action secondAction =
- createActionBuilder("same", R.drawable.ic_account_circle, pendingIntent)
- .build();
+ createActionBuilder("same", com.android.settingslib.R.drawable.ic_account_circle,
+ pendingIntent).build();
assertThat(NotificationUiAdjustment.needReinflate(
createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
deleted file mode 100644
index 1f52b9cc4195..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.CommunalStateController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-
-@SmallTest
-public class CommunalCoordinatorTest extends SysuiTestCase {
- private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-
- @Mock
- CommunalStateController mCommunalStateController;
- @Mock
- NotificationEntryManager mNotificationEntryManager;
- @Mock
- NotificationLockscreenUserManager mNotificationLockscreenUserManager;
- @Mock
- NotifPipeline mNotifPipeline;
- @Mock
- NotificationEntry mNotificationEntry;
- @Mock
- Pluggable.PluggableListener mFilterListener;
-
- CommunalCoordinator mCoordinator;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mCoordinator = new CommunalCoordinator(mExecutor, mNotificationEntryManager,
- mNotificationLockscreenUserManager, mCommunalStateController);
- }
-
- @Test
- public void testNotificationSuppressionInCommunal() {
- mCoordinator.attach(mNotifPipeline);
- final ArgumentCaptor<CommunalStateController.Callback> stateCallbackCaptor =
- ArgumentCaptor.forClass(CommunalStateController.Callback.class);
- verify(mCommunalStateController).addCallback(stateCallbackCaptor.capture());
-
- final CommunalStateController.Callback stateCallback = stateCallbackCaptor.getValue();
-
- final ArgumentCaptor<NotifFilter> filterCaptor =
- ArgumentCaptor.forClass(NotifFilter.class);
- verify(mNotifPipeline).addPreGroupFilter(filterCaptor.capture());
-
- final NotifFilter filter = filterCaptor.getValue();
-
- // Verify that notifications are not filtered out by default.
- assertThat(filter.shouldFilterOut(mNotificationEntry, 0)).isFalse();
-
- filter.setInvalidationListener(mFilterListener);
-
- // Verify that notifications are filtered out when communal is showing and that the filter
- // pipeline is notified.
- when(mCommunalStateController.getCommunalViewShowing()).thenReturn(true);
- stateCallback.onCommunalViewShowingChanged();
- // Make sure callback depends on executor to run.
- verify(mFilterListener, never()).onPluggableInvalidated(any());
- verify(mNotificationEntryManager, never()).updateNotifications(any());
-
- mExecutor.runAllReady();
-
- verify(mFilterListener).onPluggableInvalidated(any());
- verify(mNotificationEntryManager).updateNotifications(any());
- assertThat(filter.shouldFilterOut(mNotificationEntry, 0)).isTrue();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 1561b5a5d22e..bf16e0ab39c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -69,11 +69,11 @@ import com.android.systemui.statusbar.notification.collection.render.SectionHead
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -278,18 +278,17 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mStateListenerArgumentCaptor.capture(), anyInt());
StatusBarStateController.StateListener stateListener =
mStateListenerArgumentCaptor.getValue();
- when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
stateListener.onStateChanged(SHADE);
mController.getView().removeAllViews();
- mController.setQsExpanded(false);
+ mController.setQsFullScreen(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
/* notifVisibleInShade= */ false);
- mController.setQsExpanded(true);
+ mController.setQsFullScreen(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
@@ -411,11 +410,11 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
boolean toShow) {
if (toShow) {
statusBarStateListener.onStateChanged(SHADE);
- mController.setQsExpanded(false);
+ mController.setQsFullScreen(false);
mController.getView().removeAllViews();
} else {
statusBarStateListener.onStateChanged(KEYGUARD);
- mController.setQsExpanded(true);
+ mController.setQsFullScreen(true);
mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 79880229e8d5..6842295ec5d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -28,7 +28,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -88,12 +87,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.communal.CommunalHostView;
-import com.android.systemui.communal.CommunalHostViewController;
-import com.android.systemui.communal.CommunalSource;
-import com.android.systemui.communal.CommunalSourceMonitor;
-import com.android.systemui.communal.CommunalStateController;
-import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
@@ -157,7 +150,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
-import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Optional;
@@ -271,21 +263,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock
- private CommunalViewComponent.Factory mCommunalViewComponentFactory;
- @Mock
- private CommunalViewComponent mCommunalViewComponent;
- @Mock
- private CommunalHostViewController mCommunalHostViewController;
- @Mock
- private CommunalSourceMonitor mCommunalSourceMonitor;
- @Mock
- private CommunalSource mCommunalSource;
- @Mock
- private CommunalHostView mCommunalHostView;
- @Mock
- private CommunalStateController mCommunalStateController;
- private CommunalStateController.Callback mCommunalStateControllerCallback;
- @Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock
private KeyguardStatusViewController mKeyguardStatusViewController;
@@ -401,7 +378,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
- when(mView.findViewById(R.id.communal_host)).thenReturn(mCommunalHostView);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
.thenReturn(mHeadsUpCallback);
@@ -467,10 +443,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
- when(mCommunalViewComponentFactory.build(any()))
- .thenReturn(mCommunalViewComponent);
- when(mCommunalViewComponent.getCommunalHostViewController())
- .thenReturn(mCommunalHostViewController);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
.thenReturn(mKeyguardStatusView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
@@ -499,13 +471,13 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
- mCommunalStateController, mKeyguardStateController,
+ mKeyguardStateController,
mStatusBarStateController,
mStatusBarWindowStateController,
mNotificationShadeWindowController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
- mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController,
+ mMetricsLogger, mActivityManager, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
@@ -515,7 +487,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mKeyguardQsUserSwitchComponentFactory,
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
- mCommunalViewComponentFactory,
mLockscreenShadeTransitionController,
mGroupManager,
mNotificationAreaController,
@@ -1021,81 +992,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
- @Test
- public void testCommunalhostViewControllerInit() {
- verify(mCommunalHostViewController, times(1)).init();
- clearInvocations(mCommunalHostViewController);
- givenViewAttached();
- verify(mCommunalHostViewController, never()).init();
- }
-
- @Test
- public void testCommunalSourceListening() {
- final ArgumentCaptor<CommunalSourceMonitor.Callback> monitorCallback =
- ArgumentCaptor.forClass(CommunalSourceMonitor.Callback.class);
-
- givenViewAttached();
- verify(mCommunalSourceMonitor).addCallback(monitorCallback.capture());
-
- final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
- ArgumentCaptor.forClass(WeakReference.class);
-
- monitorCallback.getValue().onSourceAvailable(new WeakReference<>(mCommunalSource));
- mExecutor.runAllReady();
- verify(mCommunalHostViewController).show(sourceCapture.capture());
- assertThat(sourceCapture.getValue().get()).isEqualTo(mCommunalSource);
-
- clearInvocations(mCommunalHostViewController);
- givenViewDetached();
- verify(mCommunalSourceMonitor).removeCallback(any());
- }
-
- @Test
- public void testKeyguardStatusViewUpdatedWithCommunalPresence() {
- givenViewAttached();
-
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
- updateMultiUserSetting(true);
-
- ArgumentCaptor<CommunalStateController.Callback> communalCallbackCapture =
- ArgumentCaptor.forClass(CommunalStateController.Callback.class);
- verify(mCommunalStateController).addCallback(communalCallbackCapture.capture());
- final CommunalStateController.Callback communalStateControllerCallback =
- communalCallbackCapture.getValue();
-
- clearInvocations(mKeyguardStatusViewController, mKeyguardUserSwitcherController);
- // Ensure changes in communal visibility leads to setting the keyguard status view
- // visibility.
- communalStateControllerCallback.onCommunalViewShowingChanged();
- verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility(anyInt(),
- anyBoolean(), anyBoolean(), anyInt());
- verify(mKeyguardUserSwitcherController).setKeyguardUserSwitcherVisibility(anyInt(),
- anyBoolean(), anyBoolean(), anyInt());
- }
-
- @Test
- public void testCommunalAlphaUpdate() {
- // Verify keyguard content alpha changes are propagate. Note the actual value set is not
- // checked since an interpolation is applied to the incoming value.
- mNotificationPanelViewController.setKeyguardOnlyContentAlpha(0.8f);
- verify(mCommunalHostViewController).setAlpha(anyFloat());
- }
-
- @Test
- public void testCommunalPositionUpdate() {
- // Verify that the communal position is updated on interaction with the
- // NotificationPanelViewController. Note that there a number of paths where the position
- // might be updated and therefore the check isn't strictly on a single invocation.
- clearInvocations(mCommunalHostViewController);
- final View.OnLayoutChangeListener layoutChangeListener =
- mNotificationPanelViewController.createLayoutChangeListener();
- mNotificationPanelViewController.mStatusBarStateController.setState(KEYGUARD);
- layoutChangeListener.onLayoutChange(mView, 0, 0, 200, 200, 0, 0, 200, 200);
- verify(mCommunalHostViewController, atLeast(1))
- .updatePosition(anyInt(), anyBoolean());
- }
-
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 10f4435d3f97..0b25467d20d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1234,10 +1234,8 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setUnocclusionAnimationRunning(true);
- assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
- /* expansion */ 0.0f);
- assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
- /* expansion */ 1.0f);
+ assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0f, /* expansion */ 0f);
+ assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0f, /* expansion */ 1.0f);
// Verify normal behavior after
mScrimController.setUnocclusionAnimationRunning(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index 4a579cb3fc4d..f58403d3651a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -26,7 +26,6 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.CommunalStateController
import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.user.UserSwitchDialogController
@@ -55,9 +54,6 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
private lateinit var userSwitcherController: UserSwitcherController
@Mock
- private lateinit var communalStateController: CommunalStateController
-
- @Mock
private lateinit var keyguardStateController: KeyguardStateController
@Mock
@@ -99,7 +95,6 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
context.resources,
screenLifecycle,
userSwitcherController,
- communalStateController,
keyguardStateController,
falsingManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 7726938db3b0..185942e6fbc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -32,6 +32,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
@@ -66,6 +67,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock CommandQueue mCommandQueue;
@Mock ConfigurationController mConfigurationController;
+ @Mock KeyguardStateController mKeyguardStateController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock NavigationModeController mNavigationModeController;
@Mock ScreenLifecycle mScreenLifecycle;
@@ -90,9 +92,9 @@ public class WMShellTest extends SysuiTestCase {
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
Optional.of(mDragAndDrop),
- mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
- mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
- mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor);
+ mCommandQueue, mConfigurationController, mKeyguardStateController,
+ mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState,
+ mProtoTracer, mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor);
}
@Test
@@ -132,6 +134,6 @@ public class WMShellTest extends SysuiTestCase {
public void initCompatUI_registersCallbacks() {
mWMShell.initCompatUi(mCompatUI);
- verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ verify(mKeyguardStateController).addCallback(any(KeyguardStateController.Callback.class));
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 86777a2f62fd..e20b15a3e807 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1327,14 +1327,13 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
// Cancel without deleting events.
mHandler.removeCallbacks(mSendHoverEnterAndMoveDelayed);
- mSendHoverEnterAndMoveDelayed.run();
- mSendHoverEnterAndMoveDelayed.clear();
- final MotionEvent prototype = mState.getLastReceivedEvent();
- final MotionEvent rawEvent = mState.getLastReceivedRawEvent();
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIdBits = (1 << pointerId);
final int policyFlags = mState.getLastReceivedPolicyFlags();
- mSendHoverExitDelayed.post(prototype, rawEvent, pointerIdBits, policyFlags);
+ mSendHoverEnterAndMoveDelayed.setPointerIdBits(pointerIdBits);
+ mSendHoverEnterAndMoveDelayed.setPolicyFlags(policyFlags);
+ mSendHoverEnterAndMoveDelayed.run();
+ mSendHoverEnterAndMoveDelayed.clear();
}
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index 21a22f44f3dd..930f49e4d117 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -16,6 +16,9 @@
package com.android.server.backup;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
@@ -28,7 +31,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -42,8 +44,8 @@ import com.android.internal.util.Preconditions;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportConnection;
-import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportConnectionListener;
+import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.transport.TransportStats;
@@ -58,6 +60,7 @@ import java.util.function.Predicate;
/** Handles in-memory bookkeeping of all BackupTransport objects. */
public class TransportManager {
private static final String TAG = "BackupTransportManager";
+ private static final boolean MORE_DEBUG = false;
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
@@ -130,14 +133,61 @@ public class TransportManager {
}
}
+ void onPackageEnabled(String packageName) {
+ onPackageAdded(packageName);
+ }
+
+ void onPackageDisabled(String packageName) {
+ onPackageRemoved(packageName);
+ }
+
@WorkerThread
void onPackageChanged(String packageName, String... components) {
+ // Determine if the overall package has changed and not just its
+ // components - see {@link EXTRA_CHANGED_COMPONENT_NAME_LIST}. When we
+ // know a package was enabled/disabled we'll handle that directly and
+ // not continue with onPackageChanged.
+ if (components.length == 1 && components[0].equals(packageName)) {
+ int enabled;
+ try {
+ enabled = mPackageManager.getApplicationEnabledSetting(packageName);
+ } catch (IllegalArgumentException ex) {
+ // packageName doesn't exist: likely due to a race with it being uninstalled.
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was changed, but no longer exists.");
+ }
+ return;
+ }
+ switch (enabled) {
+ case COMPONENT_ENABLED_STATE_ENABLED: {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was enabled.");
+ }
+ onPackageEnabled(packageName);
+ return;
+ }
+ case COMPONENT_ENABLED_STATE_DISABLED: {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was disabled.");
+ }
+ onPackageDisabled(packageName);
+ return;
+ }
+ default: {
+ Slog.w(TAG, "Package " + packageName + " enabled setting: " + enabled);
+ return;
+ }
+ }
+ }
// Unfortunately this can't be atomic because we risk a deadlock if
// registerTransportsFromPackage() is put inside the synchronized block
Set<ComponentName> transportComponents = new ArraySet<>(components.length);
for (String componentName : components) {
transportComponents.add(new ComponentName(packageName, componentName));
}
+ if (transportComponents.isEmpty()) {
+ return;
+ }
synchronized (mTransportLock) {
mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains);
}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index daead0a5ff9d..b1f572d2a364 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -43,6 +43,8 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -62,7 +64,7 @@ public class CloudSearchManagerService extends
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
- R.string.config_defaultCloudSearchService), null,
+ R.array.config_defaultCloudSearchServices, true), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context;
@@ -70,7 +72,25 @@ public class CloudSearchManagerService extends
@Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
- return new CloudSearchPerUserService(this, mLock, resolvedUserId);
+ return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
+ }
+
+ @Override
+ protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
+ boolean disabled, String[] serviceNames) {
+ if (serviceNames == null) {
+ return new ArrayList<>();
+ }
+ List<CloudSearchPerUserService> serviceList =
+ new ArrayList<>(serviceNames.length);
+ for (int i = 0; i < serviceNames.length; i++) {
+ if (serviceNames[i] == null) {
+ continue;
+ }
+ serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
+ serviceNames[i]));
+ }
+ return serviceList;
}
@Override
@@ -111,19 +131,28 @@ public class CloudSearchManagerService extends
@NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setSource(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
- runForUserLocked("search", searchRequest.getRequestId(), (service) ->
- service.onSearchLocked(searchRequest, callBack));
+ runForUser("search", (service) -> {
+ synchronized (service.mLock) {
+ service.onSearchLocked(searchRequest, callBack);
+ }
+ });
}
@Override
public void returnResults(IBinder token, String requestId, SearchResponse response) {
- runForUserLocked("returnResults", requestId, (service) ->
- service.onReturnResultsLocked(token, requestId, response));
+ runForUser("returnResults", (service) -> {
+ synchronized (service.mLock) {
+ service.onReturnResultsLocked(token, requestId, response);
+ }
+ });
}
public void destroy(@NonNull SearchRequest searchRequest) {
- runForUserLocked("destroyCloudSearchSession", searchRequest.getRequestId(),
- (service) -> service.onDestroyLocked(searchRequest.getRequestId()));
+ runForUser("destroyCloudSearchSession", (service) -> {
+ synchronized (service.mLock) {
+ service.onDestroyLocked(searchRequest.getRequestId());
+ }
+ });
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -134,8 +163,7 @@ public class CloudSearchManagerService extends
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void runForUserLocked(@NonNull final String func,
- @NonNull final String requestId,
+ private void runForUser(@NonNull final String func,
@NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -143,7 +171,7 @@ public class CloudSearchManagerService extends
null, null);
if (DEBUG) {
- Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
@@ -160,8 +188,11 @@ public class CloudSearchManagerService extends
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- final CloudSearchPerUserService service = getServiceForUserLocked(userId);
- c.accept(service);
+ final List<CloudSearchPerUserService> services =
+ getServiceListForUserLocked(userId);
+ for (int i = 0; i < services.size(); i++) {
+ c.accept(services.get(i));
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
index 51f5fd9668cf..c64982d94d6d 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
@@ -54,7 +54,12 @@ public class CloudSearchManagerServiceShellCommand extends ShellCommand {
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
- mService.setTemporaryService(userId, serviceName, duration);
+ String[] services = serviceName.split(";");
+ if (services.length == 0) {
+ return 0;
+ } else {
+ mService.setTemporaryServices(userId, services, duration);
+ }
pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms");
break;
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
index 32d66afb33b9..116c7391b388 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
@@ -49,6 +49,8 @@ public class CloudSearchPerUserService extends
@GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE);
+ private final String mServiceName;
+ private final ComponentName mRemoteComponentName;
@Nullable
@GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService;
@@ -60,8 +62,10 @@ public class CloudSearchPerUserService extends
private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master,
- Object lock, int userId) {
+ Object lock, int userId, String serviceName) {
super(master, lock, userId);
+ mServiceName = serviceName;
+ mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
}
@Override // from PerUserSystemService
@@ -108,7 +112,7 @@ public class CloudSearchPerUserService extends
? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
- String remoteServicePackageName = getServiceComponentName().getPackageName();
+ String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted.
boolean wantedProvider = true;
if (filterList.length() > 0) {
@@ -150,11 +154,19 @@ public class CloudSearchPerUserService extends
/**
* Used to return results back to the clients.
*/
+ @GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId,
@NonNull SearchResponse response) {
+ if (mRemoteService == null) {
+ return;
+ }
+ ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
+ if (serviceInterface == null || token != serviceInterface.asBinder()) {
+ return;
+ }
if (mCallbackQueue.containsKey(requestId)) {
- response.setSource(mRemoteService.getComponentName().getPackageName());
+ response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
@@ -163,6 +175,10 @@ public class CloudSearchPerUserService extends
sessionInfo.mCallback.onSearchFailed(response);
}
} catch (RemoteException e) {
+ if (mMaster.debug) {
+ Slog.e(TAG, "Exception in posting results");
+ e.printStackTrace();
+ }
onDestroyLocked(requestId);
}
}
@@ -297,7 +313,7 @@ public class CloudSearchPerUserService extends
@Nullable
private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) {
- final String serviceName = getComponentNameLocked();
+ final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set");
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index c9d704084c19..2068e6d380b7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -45,6 +45,7 @@ import android.util.SparseArray;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
@@ -68,6 +69,7 @@ import java.util.function.Consumer;
* @hide Only for use within the system server.
*/
public abstract class PackageManagerInternal {
+
@IntDef(prefix = "PACKAGE_", value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
@@ -902,6 +904,11 @@ public abstract class PackageManagerInternal {
public abstract void freeStorage(String volumeUuid, long bytes,
@StorageManager.AllocateFlags int flags) throws IOException;
+ /**
+ * Blocking call to clear all cached app data above quota.
+ */
+ public abstract void freeAllAppCacheAboveQuota(@NonNull String volumeUuid) throws IOException;
+
/** Returns {@code true} if the specified component is enabled and matches the given flags. */
public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component,
@PackageManager.ComponentInfoFlagsBits long flags, int userId);
@@ -1362,4 +1369,8 @@ public abstract class PackageManagerInternal {
*/
@NonNull
public abstract PackageDataSnapshot snapshot();
+
+ public abstract void shutdown();
+
+ public abstract DynamicCodeLogger getDynamicCodeLogger();
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d4764a61566e..4628a1ff073b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -176,9 +176,6 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -198,10 +195,6 @@ import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-
/**
* Service responsible for various storage media. Connects to {@code vold} to
* watch for and manage dynamically added storage, such as SD cards and USB mass
@@ -3130,8 +3123,8 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
- public void mountObb(String rawPath, String canonicalPath, String key,
- IObbActionListener token, int nonce, ObbInfo obbInfo) {
+ public void mountObb(String rawPath, String canonicalPath, IObbActionListener token,
+ int nonce, ObbInfo obbInfo) {
Objects.requireNonNull(rawPath, "rawPath cannot be null");
Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
Objects.requireNonNull(token, "token cannot be null");
@@ -3140,7 +3133,7 @@ class StorageManagerService extends IStorageManager.Stub
final int callingUid = Binder.getCallingUid();
final ObbState obbState = new ObbState(rawPath, canonicalPath,
callingUid, token, nonce, null);
- final ObbAction action = new MountObbAction(obbState, key, callingUid, obbInfo);
+ final ObbAction action = new MountObbAction(obbState, callingUid, obbInfo);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
@@ -3677,11 +3670,20 @@ class StorageManagerService extends IStorageManager.Stub
mInstaller.tryMountDataMirror(volumeUuid);
}
}
- } catch (RemoteException | Installer.InstallerException e) {
+ } catch (Exception e) {
Slog.wtf(TAG, e);
// Make sure to re-throw this exception; we must not ignore failure
// to prepare the user storage as it could indicate that encryption
// wasn't successfully set up.
+ //
+ // Very unfortunately, these errors need to be ignored for broken
+ // users that already existed on-disk from older Android versions.
+ UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+ if (umInternal.shouldIgnorePrepareStorageErrors(userId)) {
+ Slog.wtf(TAG, "ignoring error preparing storage for existing user " + userId
+ + "; device may be insecure!");
+ return;
+ }
throw new RuntimeException(e);
}
}
@@ -4586,13 +4588,11 @@ class StorageManagerService extends IStorageManager.Stub
}
class MountObbAction extends ObbAction {
- private final String mKey;
private final int mCallingUid;
private ObbInfo mObbInfo;
- MountObbAction(ObbState obbState, String key, int callingUid, ObbInfo obbInfo) {
+ MountObbAction(ObbState obbState, int callingUid, ObbInfo obbInfo) {
super(obbState);
- mKey = key;
mCallingUid = callingUid;
mObbInfo = obbInfo;
}
@@ -4615,29 +4615,8 @@ class StorageManagerService extends IStorageManager.Stub
"Attempt to mount OBB which is already mounted: " + mObbInfo.filename);
}
- final String hashedKey;
- final String binderKey;
- if (mKey == null) {
- hashedKey = "none";
- binderKey = "";
- } else {
- try {
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
-
- KeySpec ks = new PBEKeySpec(mKey.toCharArray(), mObbInfo.salt,
- PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE);
- SecretKey key = factory.generateSecret(ks);
- BigInteger bi = new BigInteger(key.getEncoded());
- hashedKey = bi.toString(16);
- binderKey = hashedKey;
- } catch (GeneralSecurityException e) {
- throw new ObbException(ERROR_INTERNAL, e);
- }
- }
-
try {
- mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey,
- mObbState.ownerGid);
+ mObbState.volId = mVold.createObb(mObbState.canonicalPath, mObbState.ownerGid);
mVold.mount(mObbState.volId, 0, -1, null);
if (DEBUG_OBB)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 47f9fd535801..9512e1dafe7e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,6 +79,7 @@ import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
import static android.os.Process.ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS;
import static android.os.Process.ZYGOTE_PROCESS;
import static android.os.Process.getTotalMemory;
+import static android.os.Process.isSdkSandboxUid;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
import static android.os.Process.killProcessQuiet;
@@ -8561,6 +8562,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (process.info.isInstantApp()) {
sb.append("Instant-App: true\n");
}
+ if (isSdkSandboxUid(process.uid)) {
+ sb.append("SdkSandbox: true\n");
+ }
}
}
@@ -9752,7 +9756,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (info.deniedPermissions != null) {
for (int j = 0; j < info.deniedPermissions.size(); j++) {
pw.print(" deny: ");
- pw.println(info.deniedPermissions.valueAt(i));
+ pw.println(info.deniedPermissions.valueAt(j));
}
}
}
@@ -16998,7 +17002,17 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void addPendingTopUid(int uid, int pid) {
- mPendingStartActivityUids.add(uid, pid);
+ final boolean isNewPending = mPendingStartActivityUids.add(uid, pid);
+ // If the next top activity is in cached and frozen mode, WM should raise its priority
+ // to unfreeze it. This is done by calling AMS.updateOomAdj that will lower its oom adj.
+ // However, WM cannot hold the AMS clock here so the updateOomAdj operation is performed
+ // in a separate thread asynchronously. Therefore WM can't guarantee AMS will unfreeze
+ // next top activity on time. This race will fail the following binder transactions WM
+ // sends to the activity. After this race issue between WM/ATMS and AMS is solved, this
+ // workaround can be removed. (b/213288355)
+ if (isNewPending && mOomAdjuster != null) { // It can be null in unit test.
+ mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 7579d2bc78fd..7cd45fed1498 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -1217,7 +1217,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
mDefaultBgCurrentDrainBgRestrictedThreshold =
isLowRamDeviceStatic() ? val[1] : val[0];
mDefaultBgCurrentDrainWindowMs = resources.getInteger(
- R.integer.config_bg_current_drain_window);
+ R.integer.config_bg_current_drain_window) * 1_000;
val = getFloatArray(resources.obtainTypedArray(
R.array.config_bg_current_drain_high_threshold_to_restricted_bucket));
mDefaultBgCurrentDrainRestrictedBucketHighThreshold =
@@ -1227,9 +1227,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
mDefaultBgCurrentDrainBgRestrictedHighThreshold =
isLowRamDeviceStatic() ? val[1] : val[0];
mDefaultBgCurrentDrainMediaPlaybackMinDuration = resources.getInteger(
- R.integer.config_bg_current_drain_media_playback_min_duration);
+ R.integer.config_bg_current_drain_media_playback_min_duration) * 1_000;
mDefaultBgCurrentDrainLocationMinDuration = resources.getInteger(
- R.integer.config_bg_current_drain_location_min_duration);
+ R.integer.config_bg_current_drain_location_min_duration) * 1_000;
mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled = resources.getBoolean(
R.bool.config_bg_current_drain_event_duration_based_threshold_enabled);
mDefaultCurrentDrainTypesToRestrictedBucket = resources.getInteger(
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index fc29982dc7f0..d84c1fcd54fb 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -1002,6 +1002,7 @@ public final class AppExitInfoTracker {
info.setPackageName(app.info.packageName);
info.setPackageList(app.getPackageList());
info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
+ info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
info.setStatus(0);
info.setImportance(procStateToImportance(app.mState.getReportedProcState()));
info.setPss(app.mProfile.getLastPss());
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index b18d390476f0..ff569a681a4e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -183,6 +183,8 @@ public final class CachedAppOptimizer {
private final ActivityManagerGlobalLock mProcLock;
+ private final Object mFreezerLock = new Object();
+
private final OnPropertiesChangedListener mOnFlagsChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -949,8 +951,8 @@ public final class CachedAppOptimizer {
}
}
- @GuardedBy({"mAm", "mProcLock"})
- void unfreezeAppLSP(ProcessRecord app) {
+ @GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
+ void unfreezeAppInternalLSP(ProcessRecord app) {
final int pid = app.getPid();
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (opt.isPendingFreeze()) {
@@ -1035,6 +1037,42 @@ public final class CachedAppOptimizer {
}
}
+ @GuardedBy({"mAm", "mProcLock"})
+ void unfreezeAppLSP(ProcessRecord app) {
+ synchronized (mFreezerLock) {
+ unfreezeAppInternalLSP(app);
+ }
+ }
+
+ /**
+ * This quick function works around the race condition between WM/ATMS and AMS, allowing
+ * the former to directly unfreeze a frozen process before the latter runs updateOomAdj.
+ * After the race issue is solved, this workaround can be removed. (b/213288355)
+ * The caller of this function should still trigger updateOomAdj for AMS to unfreeze the app.
+ * @param pid pid of the process to be unfrozen
+ */
+ void unfreezeProcess(int pid) {
+ synchronized (mFreezerLock) {
+ ProcessRecord app = mFrozenProcesses.get(pid);
+ if (app == null) {
+ return;
+ }
+ Slog.d(TAG_AM, "quick sync unfreeze " + pid);
+ try {
+ freezeBinder(pid, false);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
+ return;
+ }
+
+ try {
+ Process.setProcessFrozen(pid, app.uid, false);
+ } catch (Exception e) {
+ Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
+ }
+ }
+ }
+
/**
* To be called when the given app is killed.
*/
@@ -1464,8 +1502,6 @@ public final class CachedAppOptimizer {
return;
}
- Slog.d(TAG_AM, "froze " + pid + " " + name);
-
EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
// See above for why we're not taking mPhenotypeFlagLock here
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b1c91ba4a79d..81a8680cdbf0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -368,8 +368,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
// Apply any launch flags from the ActivityOptions. This is to ensure that the caller
// can specify a consistent launch mode even if the PendingIntent is immutable
- final ActivityOptions opts = options != null ? ActivityOptions.fromBundle(options)
- : null;
+ final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index 802ea001e28c..455c75b88218 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -46,10 +46,13 @@ final class PendingStartActivityUids {
mContext = context;
}
- synchronized void add(int uid, int pid) {
+ /** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */
+ synchronized boolean add(int uid, int pid) {
if (mPendingUids.get(uid) == null) {
mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime()));
+ return true;
}
+ return false;
}
synchronized void delete(int uid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6e14203774a1..48ca59dc4c64 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1890,7 +1890,7 @@ public final class ProcessList {
/** Return true if the client app for the SDK sandbox process is debuggable. */
private boolean isAppForSdkSandboxDebuggable(ProcessRecord sandboxProcess) {
// TODO (b/221004701) use client app process name
- final int appUid = Process.sdkSandboxToAppUid(sandboxProcess.uid);
+ final int appUid = Process.getAppUidForSdkSandboxUid(sandboxProcess.uid);
IPackageManager pm = mService.getPackageManager();
try {
String[] packages = pm.getPackagesForUid(appUid);
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
index 0abab6aafe73..b0a389d8ac68 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
@@ -29,6 +29,7 @@ import android.service.games.IGameSessionService;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.LocalServices;
import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
import com.android.server.wm.WindowManagerInternal;
@@ -55,7 +56,8 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide
(WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
LocalServices.getService(WindowManagerInternal.class),
new GameServiceConnector(mContext, configuration),
- new GameSessionServiceConnector(mContext, configuration));
+ new GameSessionServiceConnector(mContext, configuration),
+ new ScreenshotHelper(mContext));
}
private static final class GameServiceConnector extends ServiceConnector.Impl<IGameService> {
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index e4edf4e0f5f9..faf5c3826964 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -29,7 +29,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.games.CreateGameSessionRequest;
@@ -45,12 +48,15 @@ import android.service.games.IGameSessionService;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.infra.ServiceConnector.ServiceLifecycleCallbacks;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService;
@@ -59,6 +65,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
final class GameServiceProviderInstanceImpl implements GameServiceProviderInstance {
private static final String TAG = "GameServiceProviderInstance";
@@ -188,6 +195,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
private final IActivityTaskManager mActivityTaskManager;
private final WindowManagerService mWindowManagerService;
private final WindowManagerInternal mWindowManagerInternal;
+ private final ScreenshotHelper mScreenshotHelper;
private final ServiceConnector<IGameService> mGameServiceConnector;
private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector;
@@ -207,7 +215,8 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
@NonNull WindowManagerService windowManagerService,
@NonNull WindowManagerInternal windowManagerInternal,
@NonNull ServiceConnector<IGameService> gameServiceConnector,
- @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector) {
+ @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector,
+ @NonNull ScreenshotHelper screenshotHelper) {
mUserHandle = userHandle;
mBackgroundExecutor = backgroundExecutor;
mContext = context;
@@ -218,6 +227,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
mWindowManagerInternal = windowManagerInternal;
mGameServiceConnector = gameServiceConnector;
mGameSessionServiceConnector = gameSessionServiceConnector;
+ mScreenshotHelper = screenshotHelper;
}
@Override
@@ -651,7 +661,26 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
Slog.w(TAG, "Could not get bitmap for id: " + taskId);
callback.complete(GameScreenshotResult.createInternalErrorResult());
} else {
- callback.complete(GameScreenshotResult.createSuccessResult(bitmap));
+ final Bundle bundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(
+ bitmap);
+ final RunningTaskInfo runningTaskInfo = getRunningTaskInfoForTask(taskId);
+ if (runningTaskInfo == null) {
+ Slog.w(TAG, "Could not get running task info for id: " + taskId);
+ callback.complete(GameScreenshotResult.createInternalErrorResult());
+ }
+ final Rect crop = runningTaskInfo.configuration.windowConfiguration.getBounds();
+ final Consumer<Uri> completionConsumer = (uri) -> {
+ if (uri == null) {
+ callback.complete(GameScreenshotResult.createInternalErrorResult());
+ } else {
+ callback.complete(GameScreenshotResult.createSuccessResult());
+ }
+ };
+ mScreenshotHelper.provideScreenshot(bundle, crop, Insets.NONE, taskId,
+ mUserHandle.getIdentifier(), gameSessionRecord.getComponentName(),
+ WindowManager.ScreenshotSource.SCREENSHOT_OTHER,
+ BackgroundThread.getHandler(),
+ completionConsumer);
}
});
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 82476d8d1df8..d01be5820d34 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -127,6 +127,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HwBinder;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -10471,6 +10472,25 @@ public class AudioService extends IAudioService.Stub
return mMediaFocusControl.sendFocusLoss(focusLoser);
}
+ private static final String[] HAL_VERSIONS = new String[] {"7.1", "7.0", "6.0", "4.0", "2.0"};
+
+ /** @see AudioManager#getHalVersion */
+ public @Nullable String getHalVersion() {
+ for (String version : HAL_VERSIONS) {
+ try {
+ HwBinder.getService(
+ String.format("android.hardware.audio@%s::IDevicesFactory", version),
+ "default");
+ return version;
+ } catch (NoSuchElementException e) {
+ // Ignore, the specified HAL interface is not found.
+ } catch (RemoteException re) {
+ Log.e(TAG, "Remote exception when getting hardware audio service:", re);
+ }
+ }
+ return null;
+ }
+
/** see AudioManager.hasRegisteredDynamicPolicy */
public boolean hasRegisteredDynamicPolicy() {
synchronized (mAudioPolicies) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 653776b3ca65..79e3bf53acd3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -140,12 +140,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@Override
public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) {
- // For UDFPS, notify SysUI that the illumination can be turned off.
- // See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
- if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
- mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
- }
-
+ // For UDFPS, notify SysUI with acquiredInfo, so that the illumination can be turned off
+ // for most ACQUIRED messages. See BiometricFingerprintConstants#FingerprintAcquired
+ mSensorOverlays.ifUdfps(controller -> controller.onAcquired(getSensorId(), acquiredInfo));
super.onAcquired(acquiredInfo, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index c92d599d68e6..bb1fed0bfecc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -114,12 +114,16 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@Override
public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) {
+ boolean acquiredGood =
+ acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
// For UDFPS, notify SysUI that the illumination can be turned off.
// See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
- if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
- && mSensorProps.isAnyUdfpsType()) {
- vibrateSuccess();
- mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
+ if (mSensorProps.isAnyUdfpsType()) {
+ if (acquiredGood) {
+ vibrateSuccess();
+ }
+ mSensorOverlays.ifUdfps(
+ controller -> controller.onAcquired(getSensorId(), acquiredInfo));
}
mSensorOverlays.ifUdfps(controller -> {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index ecb43f601a86..7db99f1da130 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -184,6 +184,9 @@ public class CameraServiceProxy extends SystemService
// Must be equal to number of CameraStreamProto in CameraActionEvent
private static final int MAX_STREAM_STATISTICS = 5;
+ private static final float MIN_PREVIEW_FPS = 30.0f;
+ private static final float MAX_PREVIEW_FPS = 60.0f;
+
private final Context mContext;
private final ServiceThread mHandlerThread;
private final Handler mHandler;
@@ -821,6 +824,7 @@ public class CameraServiceProxy extends SystemService
Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+ ", height " + streamProtos[i].height
+ ", format " + streamProtos[i].format
+ + ", maxPreviewFps " + streamStats.getMaxPreviewFps()
+ ", dataSpace " + streamProtos[i].dataSpace
+ ", usage " + streamProtos[i].usage
+ ", requestCount " + streamProtos[i].requestCount
@@ -1015,6 +1019,11 @@ public class CameraServiceProxy extends SystemService
return false;
}
+ private float getMinFps(CameraSessionStats cameraState) {
+ float maxFps = cameraState.getMaxPreviewFps();
+ return Math.max(Math.min(maxFps, MAX_PREVIEW_FPS), MIN_PREVIEW_FPS);
+ }
+
private void updateActivityCount(CameraSessionStats cameraState) {
String cameraId = cameraState.getCameraId();
int newCameraState = cameraState.getNewCameraState();
@@ -1068,7 +1077,9 @@ public class CameraServiceProxy extends SystemService
if (!alreadyActivePackage) {
WindowManagerInternal wmi =
LocalServices.getService(WindowManagerInternal.class);
- wmi.addNonHighRefreshRatePackage(clientName);
+ float minFps = getMinFps(cameraState);
+ wmi.addRefreshRateRangeForPackage(clientName,
+ minFps, MAX_PREVIEW_FPS);
}
// Update activity events
@@ -1107,7 +1118,7 @@ public class CameraServiceProxy extends SystemService
if (!stillActivePackage) {
WindowManagerInternal wmi =
LocalServices.getService(WindowManagerInternal.class);
- wmi.removeNonHighRefreshRatePackage(clientName);
+ wmi.removeRefreshRateRangeForPackage(clientName);
}
}
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
index 11c451e01d4c..28c7cad3b184 100644
--- a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -54,8 +54,8 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
return bits;
}
- private boolean isPipeOpened() {
- return mPipe != null;
+ private synchronized FileDescriptor getPipeFD() {
+ return mPipe;
}
private synchronized boolean openPipe() {
@@ -107,14 +107,16 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
return msg;
}
- private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+ private static void sendMessage(
+ final FileDescriptor fd,
+ final byte[] msg) throws ErrnoException, InterruptedIOException {
final byte[] lengthBits = new byte[4];
final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(msg.length);
- Os.write(mPipe, lengthBits, 0, lengthBits.length);
- Os.write(mPipe, msg, 0, msg.length);
+ Os.write(fd, lengthBits, 0, lengthBits.length);
+ Os.write(fd, msg, 0, msg.length);
}
EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
@@ -162,17 +164,22 @@ class EmulatorClipboardMonitor implements Consumer<ClipData> {
}
private void setHostClipboardImpl(final String value) {
- if (LOG_CLIBOARD_ACCESS) {
- Slog.i(TAG, "Setting the host clipboard to '" + value + "'");
- }
+ final FileDescriptor pipeFD = getPipeFD();
- try {
- if (isPipeOpened()) {
- sendMessage(value.getBytes());
- }
- } catch (ErrnoException | InterruptedIOException e) {
- Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
- } catch (IllegalArgumentException e) {
+ if (pipeFD != null) {
+ Thread t = new Thread(() -> {
+ if (LOG_CLIBOARD_ACCESS) {
+ Slog.i(TAG, "Setting the host clipboard to '" + value + "'");
+ }
+
+ try {
+ sendMessage(pipeFD, value.getBytes());
+ } catch (ErrnoException | InterruptedIOException e) {
+ Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ }
+ });
+ t.start();
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 682f0dfe067c..c0df095c3289 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1320,6 +1320,7 @@ public class Vpn {
.setLegacyTypeName("VPN")
.setBypassableVpn(mConfig.allowBypass && !mLockdown)
.setVpnRequiresValidation(mConfig.requiresInternetValidation)
+ .setLocalRoutesExcludedForVpn(mConfig.excludeLocalRoutes)
.build();
capsBuilder.setOwnerUid(mOwnerUID);
@@ -3386,6 +3387,7 @@ public class Vpn {
mConfig.startTime = SystemClock.elapsedRealtime();
mConfig.proxyInfo = profile.proxy;
mConfig.requiresInternetValidation = profile.requiresInternetValidation;
+ mConfig.excludeLocalRoutes = profile.excludeLocalRoutes;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c0950bf13a1c..d249d578cf67 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -654,14 +654,17 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected int handleTextViewOn(HdmiCecMessage message) {
assertRunOnServiceThread();
- // Note that <Text View On> (and <Image View On>) command won't be handled here in
- // most cases. A dedicated microcontroller should be in charge while Android system
- // is in sleep mode, and the command need not be passed up to this service.
- // The only situation where the command reaches this handler is that sleep mode is
- // implemented in such a way that Android system is not really put to standby mode
- // but only the display is set to blank. Then the command leads to the effect of
+ // Note that if the device is in sleep mode, the <Text View On> (and <Image View On>)
+ // command won't be handled here in most cases. A dedicated microcontroller should be in
+ // charge while the Android system is in sleep mode, and the command doesn't need to be
+ // passed up to this service.
+ // The only situations where the command reaches this handler are
+ // 1. if sleep mode is implemented in such a way that Android system is not really put to
+ // standby mode but only the display is set to blank. Then the command leads to
// turning on the display by the invocation of PowerManager.wakeUp().
- if (mService.isPowerStandbyOrTransient() && getAutoWakeup()) {
+ // 2. if the device is in dream mode, not sleep mode. Then this command leads to
+ // waking up the device from dream mode by the invocation of PowerManager.wakeUp().
+ if (getAutoWakeup()) {
mService.wakeUp();
}
return Constants.HANDLED;
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9e00f95c9c6f..34e3ce1ab641 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -48,6 +48,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -168,10 +169,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private final SparseBooleanArray mDisabledByUserRestriction;
/**
- * Cache of services per user id.
+ * Cache of service list per user id.
*/
@GuardedBy("mLock")
- private final SparseArray<S> mServicesCache = new SparseArray<>();
+ private final SparseArray<List<S>> mServicesCacheList = new SparseArray<>();
/**
* Value that determines whether the per-user service should be removed from the cache when its
@@ -252,8 +253,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) {
mServiceNameResolver.setOnTemporaryServiceNameChangedCallback(
- (u, s, t) -> onServiceNameChanged(u, s, t));
-
+ this::onServiceNameChanged);
}
if (disallowProperty == null) {
mDisabledByUserRestriction = null;
@@ -308,7 +308,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override // from SystemService
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
- removeCachedServiceLocked(user.getUserIdentifier());
+ removeCachedServiceListLocked(user.getUserIdentifier());
}
}
@@ -386,21 +386,58 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
}
}
/**
+ * Temporarily sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + Arrays.toString(componentNames)
+ + " for " + durationMs + "ms");
+ if (mServiceNameResolver == null) {
+ return;
+ }
+ enforceCallingPermissionForManagement();
+
+ Objects.requireNonNull(componentNames);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S oldService = peekServiceForUserLocked(userId);
+ if (oldService != null) {
+ oldService.removeSelfFromCache();
+ }
+ mServiceNameResolver.setTemporaryServices(userId, componentNames, durationMs);
+ }
+ }
+
+ /**
* Sets whether the default service should be used.
*
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @throws SecurityException if caller is not allowed to manage this service's settings.
- *
* @return whether the enabled state changed.
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
@@ -420,7 +457,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
// Must update the service on cache so its initialization code is triggered
@@ -501,6 +538,21 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
+ * Creates a new service list that will be added to the cache.
+ *
+ * @param resolvedUserId the resolved user id for the service.
+ * @param disabled whether the service is currently disabled (due to {@link UserManager}
+ * restrictions).
+ * @return a new instance.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected List<S> newServiceListLocked(@UserIdInt int resolvedUserId, boolean disabled,
+ String[] serviceNames) {
+ throw new UnsupportedOperationException("newServiceListLocked not implemented. ");
+ }
+
+ /**
* Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}, which are automatically handled).
@@ -516,7 +568,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}.
- *
*/
@SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@@ -527,7 +578,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* Callback for Settings changes that were registered though
* {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
*
- * @param userId user associated with the change
+ * @param userId user associated with the change
* @param property Settings property changed.
*/
protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
@@ -539,18 +590,35 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) {
+ List<S> services = getServiceListForUserLocked(userId);
+ return services == null || services.size() == 0 ? null : services.get(0);
+ }
+
+ /**
+ * Gets the service instance list for a user, creating instances if not present in the cache.
+ */
+ @GuardedBy("mLock")
+ protected List<S> getServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- S service = mServicesCache.get(resolvedUserId);
- if (service == null) {
+ List<S> services = mServicesCacheList.get(resolvedUserId);
+ if (services == null || services.size() == 0) {
final boolean disabled = isDisabledLocked(userId);
- service = newServiceLocked(resolvedUserId, disabled);
+ if (mServiceNameResolver != null && mServiceNameResolver.isConfiguredInMultipleMode()) {
+ services = newServiceListLocked(resolvedUserId, disabled,
+ mServiceNameResolver.getServiceNameList(userId));
+ } else {
+ services = new ArrayList<>();
+ services.add(newServiceLocked(resolvedUserId, disabled));
+ }
if (!disabled) {
- onServiceEnabledLocked(service, resolvedUserId);
+ for (int i = 0; i < services.size(); i++) {
+ onServiceEnabledLocked(services.get(i), resolvedUserId);
+ }
}
- mServicesCache.put(userId, service);
+ mServicesCacheList.put(userId, services);
}
- return service;
+ return services;
}
/**
@@ -560,9 +628,20 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@Nullable
protected S peekServiceForUserLocked(@UserIdInt int userId) {
+ List<S> serviceList = peekServiceListForUserLocked(userId);
+ return serviceList == null || serviceList.size() == 0 ? null : serviceList.get(0);
+ }
+
+ /**
+ * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
+ * present in the cache.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ protected List<S> peekServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- return mServicesCache.get(resolvedUserId);
+ return mServicesCacheList.get(resolvedUserId);
}
/**
@@ -570,36 +649,59 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void updateCachedServiceLocked(@UserIdInt int userId) {
- updateCachedServiceLocked(userId, isDisabledLocked(userId));
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
/**
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
+ @GuardedBy("mLock")
protected boolean isDisabledLocked(@UserIdInt int userId) {
- return mDisabledByUserRestriction == null ? false : mDisabledByUserRestriction.get(userId);
+ return mDisabledByUserRestriction != null && mDisabledByUserRestriction.get(userId);
}
/**
* Updates a cached service for a given user.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
- if (service != null) {
- service.updateLocked(disabled);
- if (!service.isEnabledLocked()) {
- removeCachedServiceLocked(userId);
- } else {
- onServiceEnabledLocked(service, userId);
+ updateCachedServiceListLocked(userId, disabled);
+ return service;
+ }
+
+ /**
+ * Updates a cached service for a given user.
+ *
+ * @param userId user handle.
+ * @param disabled whether the user is disabled.
+ * @return service for the user.
+ */
+ @GuardedBy("mLock")
+ protected List<S> updateCachedServiceListLocked(@UserIdInt int userId, boolean disabled) {
+ final List<S> services = getServiceListForUserLocked(userId);
+ if (services == null) {
+ return null;
+ }
+ for (int i = 0; i < services.size(); i++) {
+ S service = services.get(i);
+ if (service != null) {
+ synchronized (service.mLock) {
+ service.updateLocked(disabled);
+ if (!service.isEnabledLocked()) {
+ removeCachedServiceListLocked(userId);
+ } else {
+ onServiceEnabledLocked(services.get(i), userId);
+ }
+ }
}
}
- return service;
+ return services;
}
/**
@@ -619,28 +721,32 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
+ @GuardedBy("mLock")
protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
- * Removes a cached service for a given user.
+ * Removes a cached service list for a given user.
*
* @return the removed service.
*/
@GuardedBy("mLock")
@NonNull
- protected final S removeCachedServiceLocked(@UserIdInt int userId) {
- final S service = peekServiceForUserLocked(userId);
- if (service != null) {
- mServicesCache.delete(userId);
- onServiceRemoved(service, userId);
+ protected final List<S> removeCachedServiceListLocked(@UserIdInt int userId) {
+ final List<S> services = peekServiceListForUserLocked(userId);
+ if (services != null) {
+ mServicesCacheList.delete(userId);
+ for (int i = 0; i < services.size(); i++) {
+ onServiceRemoved(services.get(i), userId);
+ }
}
- return service;
+ return services;
}
/**
* Called before the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatingLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdatingLocked(" + userId + ")");
}
@@ -648,6 +754,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdated(" + userId + ")");
}
@@ -655,6 +762,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package data that provides the service for the given user is cleared.
*/
+ @GuardedBy("mLock")
protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")");
}
@@ -662,6 +770,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is restarted.
*/
+ @GuardedBy("mLock")
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
}
@@ -679,14 +788,31 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
* that same method, or {@code super.onServiceNameChanged()}.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param serviceName the new service name.
* @param isTemporary whether the new service is temporary.
*/
protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName,
boolean isTemporary) {
synchronized (mLock) {
- updateCachedServiceLocked(userId);
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
+ }
+ }
+
+ /**
+ * Called when the service name list has changed (typically when using temporary services).
+ *
+ * <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
+ * that same method, or {@code super.onServiceNameChanged()}.
+ *
+ * @param userId user handle.
+ * @param serviceNames the new service name list.
+ * @param isTemporary whether the new service is temporary.
+ */
+ protected void onServiceNameListChanged(@UserIdInt int userId, @Nullable String[] serviceNames,
+ boolean isTemporary) {
+ synchronized (mLock) {
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
}
@@ -695,9 +821,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
- final int size = mServicesCache.size();
+ final int size = mServicesCacheList.size();
for (int i = 0; i < size; i++) {
- visitor.visit(mServicesCache.valueAt(i));
+ List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ visitor.visit(services.get(j));
+ }
}
}
@@ -706,7 +835,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void clearCacheLocked() {
- mServicesCache.clear();
+ mServicesCacheList.clear();
}
/**
@@ -757,6 +886,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
// TODO(b/117779333): support proto
+ @GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
@@ -765,40 +895,64 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
try {
// Temporarily turn on full logging;
debug = verbose = true;
- final int size = mServicesCache.size();
- pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
- pw.print(" Verbose: "); pw.println(realVerbose);
- pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags);
+ final int size = mServicesCacheList.size();
+ pw.print(prefix);
+ pw.print("Debug: ");
+ pw.print(realDebug);
+ pw.print(" Verbose: ");
+ pw.println(realVerbose);
+ pw.print("Package policy flags: ");
+ pw.println(mServicePackagePolicyFlags);
if (mUpdatingPackageNames != null) {
- pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
+ pw.print("Packages being updated: ");
+ pw.println(mUpdatingPackageNames);
}
dumpSupportedUsers(pw, prefix);
if (mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mServiceNameResolver.dumpShort(pw); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mServiceNameResolver.dumpShort(pw);
+ pw.println();
final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
- pw.print(prefix2); pw.print(userId); pw.print(": ");
- mServiceNameResolver.dumpShort(pw, userId); pw.println();
+ pw.print(prefix2);
+ pw.print(userId);
+ pw.print(": ");
+ mServiceNameResolver.dumpShort(pw, userId);
+ pw.println();
}
}
- pw.print(prefix); pw.print("Users disabled by restriction: ");
+ pw.print(prefix);
+ pw.print("Users disabled by restriction: ");
pw.println(mDisabledByUserRestriction);
- pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+ pw.print(prefix);
+ pw.print("Allow instant service: ");
+ pw.println(mAllowInstantService);
final String settingsProperty = getServiceSettingsProperty();
if (settingsProperty != null) {
- pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
+ pw.print(prefix);
+ pw.print("Settings property: ");
+ pw.println(settingsProperty);
}
- pw.print(prefix); pw.print("Cached services: ");
+ pw.print(prefix);
+ pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
} else {
pw.println(size);
for (int i = 0; i < size; i++) {
- pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
- final S service = mServicesCache.valueAt(i);
- service.dumpLocked(prefix2, pw);
+ pw.print(prefix);
+ pw.print("Service at ");
+ pw.print(i);
+ pw.println(": ");
+ final List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ S service = services.get(i);
+ synchronized (service.mLock) {
+ service.dumpLocked(prefix2, pw);
+ }
+ }
pw.println();
}
}
@@ -820,7 +974,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final int userId = getChangingUserId();
synchronized (mLock) {
if (mUpdatingPackageNames == null) {
- mUpdatingPackageNames = new SparseArray<String>(mServicesCache.size());
+ mUpdatingPackageNames = new SparseArray<String>(mServicesCacheList.size());
}
mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId);
@@ -835,7 +989,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being updated");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
!= 0) {
@@ -901,7 +1055,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
handleActiveServiceRestartedLocked(activePackageName, userId);
} else {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
} else {
handlePackageUpdateLocked(pkg);
@@ -930,7 +1084,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private void handleActiveServiceRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) {
@@ -939,6 +1093,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
+ @GuardedBy("mLock")
private void handleActiveServiceRestartedLocked(String activePackageName,
@UserIdInt int userId) {
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
@@ -952,7 +1107,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being restarted");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
if (debug) {
@@ -966,14 +1121,27 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override
public void onPackageModified(String packageName) {
- if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+ synchronized (mLock) {
+ if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
- if (mServiceNameResolver == null) {
- return;
+ if (mServiceNameResolver == null) {
+ return;
+ }
+
+ final int userId = getChangingUserId();
+ final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
+ userId);
+ if (serviceNames != null) {
+ for (int i = 0; i < serviceNames.length; i++) {
+ peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
+ }
+ }
}
+ }
- final int userId = getChangingUserId();
- final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
+ @GuardedBy("mLock")
+ private void peekAndUpdateCachedServiceLocked(String packageName, int userId,
+ String serviceName) {
if (serviceName == null) {
return;
}
@@ -997,6 +1165,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
+ @GuardedBy("mLock")
private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
@@ -1017,7 +1186,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
};
// package changes
- monitor.register(getContext(), null, UserHandle.ALL, true);
+ monitor.register(getContext(), null, UserHandle.ALL, true);
}
/**
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 757a5cca0817..b8f1db402a55 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -43,14 +43,13 @@ import java.io.PrintWriter;
*
* @param <M> "main" service class.
* @param <S> "real" service class.
- *
* @hide
*/
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
- protected final @UserIdInt int mUserId;
- protected final Object mLock;
+ @UserIdInt protected final int mUserId;
+ public final Object mLock;
protected final String mTag = getClass().getSimpleName();
protected final M mMaster;
@@ -91,14 +90,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* <p><b>MUST</b> be overridden by subclasses that bind to an
* {@link com.android.internal.infra.AbstractRemoteService}.
*
- * @throws NameNotFoundException if the service does not exist.
- * @throws SecurityException if the service does not have the proper permissions to be bound to.
- * @throws UnsupportedOperationException if subclass binds to a remote service but does not
- * overrides it.
- *
* @return new {@link ServiceInfo},
+ * @throws NameNotFoundException if the service does not exist.
+ * @throws SecurityException if the service does not have the proper permissions to
+ * be bound to.
+ * @throws UnsupportedOperationException if subclass binds to a remote service but does not
+ * overrides it.
*/
- protected @NonNull ServiceInfo newServiceInfoLocked(
+ @NonNull protected ServiceInfo newServiceInfoLocked(
@SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
throws NameNotFoundException {
throw new UnsupportedOperationException("not overridden");
@@ -137,7 +136,6 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
- *
* @return whether the disabled state changed.
*/
@GuardedBy("mLock")
@@ -154,18 +152,49 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
updateIsSetupComplete(mUserId);
mDisabled = disabled;
- updateServiceInfoLocked();
+ if (mMaster.mServiceNameResolver != null
+ && mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
+ updateServiceInfoListLocked();
+ } else {
+ updateServiceInfoLocked();
+ }
return wasEnabled != isEnabledLocked();
}
/**
* Updates the internal reference to the service info, and returns the service's component.
*/
+ @GuardedBy("mLock")
protected final ComponentName updateServiceInfoLocked() {
- ComponentName serviceComponent = null;
- if (mMaster.mServiceNameResolver != null) {
- ServiceInfo serviceInfo = null;
+ ComponentName[] componentNames = updateServiceInfoListLocked();
+ return componentNames == null || componentNames.length == 0 ? null : componentNames[0];
+ }
+
+ /**
+ * Updates the internal reference to the service info, and returns the service's component.
+ */
+ @GuardedBy("mLock")
+ protected final ComponentName[] updateServiceInfoListLocked() {
+ if (mMaster.mServiceNameResolver == null) {
+ return null;
+ }
+ if (!mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
final String componentName = getComponentNameLocked();
+ return new ComponentName[] { getServiceComponent(componentName) };
+ }
+ final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
+ mUserId);
+ ComponentName[] serviceComponents = new ComponentName[componentNames.length];
+ for (int i = 0; i < componentNames.length; i++) {
+ serviceComponents[i] = getServiceComponent(componentNames[i]);
+ }
+ return serviceComponents;
+ }
+
+ private ComponentName getServiceComponent(String componentName) {
+ synchronized (mLock) {
+ ServiceInfo serviceInfo = null;
+ ComponentName serviceComponent = null;
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -196,14 +225,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
mServiceInfo = null;
}
+ return serviceComponent;
}
- return serviceComponent;
}
/**
* Gets the user associated with this service.
*/
- public final @UserIdInt int getUserId() {
+ @UserIdInt public final int getUserId() {
return mUserId;
}
@@ -229,15 +258,34 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets the current name of the service, which is either the default service or the
- * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
- protected final @Nullable String getComponentNameLocked() {
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
}
/**
+ * Gets the current name of the service, which is either the default service or the
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameForMultipleLocked(String serviceName) {
+ String[] services = mMaster.mServiceNameResolver.getServiceNameList(mUserId);
+ for (int i = 0; i < services.length; i++) {
+ if (serviceName.equals(services[i])) {
+ return services[i];
+ }
+ }
+ return null;
+ }
+
+ /**
* Checks whether the current service for the user was temporarily set.
*/
+ @GuardedBy("mLock")
public final boolean isTemporaryServiceSetLocked() {
return mMaster.mServiceNameResolver.isTemporary(mUserId);
}
@@ -245,6 +293,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Resets the temporary service implementation to the default component.
*/
+ @GuardedBy("mLock")
protected final void resetTemporaryServiceLocked() {
mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
}
@@ -268,6 +317,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
return mServiceInfo == null ? null : mServiceInfo.getComponentName();
}
}
+
/**
* Gets the name of the of the app this service binds to, or {@code null} if the service is
* disabled.
@@ -303,8 +353,10 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Removes the service from the main service's cache.
*/
- protected final void removeSelfFromCacheLocked() {
- mMaster.removeCachedServiceLocked(mUserId);
+ protected final void removeSelfFromCache() {
+ synchronized (mMaster.mLock) {
+ mMaster.removeCachedServiceListLocked(mUserId);
+ }
}
/**
@@ -327,6 +379,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* Gets the target SDK level of the service this service binds to,
* or {@code 0} if the service is disabled.
*/
+ @GuardedBy("mLock")
public final int getTargedSdkLocked() {
return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
}
@@ -334,6 +387,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets whether the device already finished setup.
*/
+ @GuardedBy("mLock")
protected final boolean isSetupCompletedLocked() {
return mSetupComplete;
}
@@ -348,19 +402,32 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.println(mUserId);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
- pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
+ pw.print(prefix);
+ pw.print("Service Label: ");
+ pw.println(getServiceLabelLocked());
+ pw.print(prefix);
+ pw.print("Target SDK: ");
+ pw.println(getTargedSdkLocked());
}
if (mMaster.mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mMaster.mServiceNameResolver.dumpShort(pw, mUserId);
+ pw.println();
}
- pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
- pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
+ pw.print(prefix);
+ pw.print("Disabled by UserManager: ");
+ pw.println(mDisabled);
+ pw.print(prefix);
+ pw.print("Setup complete: ");
+ pw.println(mSetupComplete);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service UID: ");
+ pw.print(prefix);
+ pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
pw.println();
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 35d59561fdeb..db2cb52d778e 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -15,6 +15,7 @@
*/
package com.android.server.infra;
+import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -33,6 +34,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -47,20 +49,20 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
/** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
- private final @NonNull Context mContext;
- private final @NonNull Object mLock = new Object();
- private final @StringRes int mResourceId;
- private @Nullable NameResolverListener mOnSetCallback;
-
+ @NonNull private final Context mContext;
+ @NonNull private final Object mLock = new Object();
+ @StringRes private final int mStringResourceId;
+ @ArrayRes private final int mArrayResourceId;
+ private final boolean mIsMultiple;
/**
- * Map of temporary service name set by {@link #setTemporaryService(int, String, int)},
+ * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
* keyed by {@code userId}.
*
- * <p>Typically used by Shell command and/or CTS tests.
+ * <p>Typically used by Shell command and/or CTS tests to configure temporary services if
+ * mIsMultiple is true.
*/
@GuardedBy("mLock")
- private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
-
+ private final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
/**
* Map of default services that have been disabled by
* {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
@@ -69,7 +71,7 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
-
+ @Nullable private NameResolverListener mOnSetCallback;
/**
* When the temporary service will expire (and reset back to the default).
*/
@@ -85,7 +87,22 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@StringRes int resourceId) {
mContext = context;
- mResourceId = resourceId;
+ mStringResourceId = resourceId;
+ mArrayResourceId = -1;
+ mIsMultiple = false;
+ }
+
+ public FrameworkResourcesServiceNameResolver(@NonNull Context context,
+ @ArrayRes int resourceId, boolean isMultiple) {
+ if (!isMultiple) {
+ throw new UnsupportedOperationException("Please use "
+ + "FrameworkResourcesServiceNameResolver(context, @StringRes int) constructor "
+ + "if single service mode is requested.");
+ }
+ mContext = context;
+ mStringResourceId = -1;
+ mArrayResourceId = resourceId;
+ mIsMultiple = true;
}
@Override
@@ -96,22 +113,31 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
@Override
+ public String getServiceName(@UserIdInt int userId) {
+ String[] serviceNames = getServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+ }
+
+ @Override
public String getDefaultServiceName(@UserIdInt int userId) {
- synchronized (mLock) {
- final String name = mContext.getString(mResourceId);
- return TextUtils.isEmpty(name) ? null : name;
- }
+ String[] serviceNames = getDefaultServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
}
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
@Override
- public String getServiceName(@UserIdInt int userId) {
+ public String[] getServiceNameList(int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
+ String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
// Always log it, as it should only be used on CTS or during development
- Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName
- + " for user " + userId);
- return temporaryName;
+ Slog.w(TAG, "getServiceName(): using temporary name "
+ + Arrays.toString(temporaryNames) + " for user " + userId);
+ return temporaryNames;
}
final boolean disabled = mDefaultServicesDisabled.get(userId);
if (disabled) {
@@ -120,22 +146,50 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
+ "user " + userId);
return null;
}
- return getDefaultServiceName(userId);
+ return getDefaultServiceNameList(userId);
+
+ }
+ }
+
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
+ @Override
+ public String[] getDefaultServiceNameList(int userId) {
+ synchronized (mLock) {
+ if (mIsMultiple) {
+ return mContext.getResources().getStringArray(mArrayResourceId);
+ } else {
+ final String name = mContext.getString(mStringResourceId);
+ return TextUtils.isEmpty(name) ? new String[0] : new String[] { name };
+ }
}
}
@Override
+ public boolean isConfiguredInMultipleMode() {
+ return mIsMultiple;
+ }
+
+ @Override
public boolean isTemporary(@UserIdInt int userId) {
synchronized (mLock) {
- return mTemporaryServiceNames.get(userId) != null;
+ return mTemporaryServiceNamesList.get(userId) != null;
}
}
@Override
public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
int durationMs) {
+ setTemporaryServices(userId, new String[]{componentName}, durationMs);
+ }
+
+ @Override
+ public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
synchronized (mLock) {
- mTemporaryServiceNames.put(userId, componentName);
+ mTemporaryServiceNamesList.put(userId, componentNames);
if (mTemporaryHandler == null) {
mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
@@ -155,8 +209,10 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
- notifyTemporaryServiceNameChangedLocked(userId, componentName,
- /* isTemporary= */ true);
+ for (int i = 0; i < componentNames.length; i++) {
+ notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
+ /* isTemporary= */ true);
+ }
}
}
@@ -164,8 +220,8 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public void resetTemporaryService(@UserIdInt int userId) {
synchronized (mLock) {
Slog.i(TAG, "resetting temporary service for user " + userId + " from "
- + mTemporaryServiceNames.get(userId));
- mTemporaryServiceNames.remove(userId);
+ + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
+ mTemporaryServiceNamesList.remove(userId);
if (mTemporaryHandler != null) {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
mTemporaryHandler = null;
@@ -207,16 +263,21 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public String toString() {
- return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
+ synchronized (mLock) {
+ return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
+ }
}
// TODO(b/117779333): support proto
@Override
public void dumpShort(@NonNull PrintWriter pw) {
synchronized (mLock) {
- pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
- pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
- pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size());
+ pw.print("FrameworkResourcesServiceNamer: resId=");
+ pw.print(mStringResourceId);
+ pw.print(", numberTemps=");
+ pw.print(mTemporaryServiceNamesList.size());
+ pw.print(", enabledDefaults=");
+ pw.print(mDefaultServicesDisabled.size());
}
}
@@ -224,13 +285,17 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
- pw.print("tmpName="); pw.print(temporaryName);
+ final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
+ pw.print("tmpName=");
+ pw.print(Arrays.toString(temporaryNames));
final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
- pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
+ pw.print(" (expires in ");
+ TimeUtils.formatDuration(ttl, pw);
+ pw.print("), ");
}
- pw.print("defaultName="); pw.print(getDefaultServiceName(userId));
+ pw.print("defaultName=");
+ pw.print(getDefaultServiceName(userId));
final boolean disabled = mDefaultServicesDisabled.get(userId);
pw.println(disabled ? " (disabled)" : " (enabled)");
}
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index e20c45992e05..7d85fdb45266 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -34,7 +34,7 @@ public interface ServiceNameResolver {
/**
* Listener for name changes.
*/
- public interface NameResolverListener {
+ interface NameResolverListener {
/**
* The name change callback.
@@ -64,6 +64,30 @@ public interface ServiceNameResolver {
String getDefaultServiceName(@UserIdInt int userId);
/**
+ * Gets the default list of names of the services for the given user.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ @Nullable
+ default String[] getDefaultServiceNameList(@UserIdInt int userId) {
+ if (isConfiguredInMultipleMode()) {
+ throw new UnsupportedOperationException("getting default service list not supported");
+ } else {
+ return new String[] { getDefaultServiceName(userId) };
+ }
+ }
+
+ /**
+ * Returns whether the resolver is configured to connect to multiple backend services.
+ * The default return type is false.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ default boolean isConfiguredInMultipleMode() {
+ return false;
+ }
+
+ /**
* Gets the current name of the service for the given user
*
* @return either the temporary name (set by
@@ -76,6 +100,18 @@ public interface ServiceNameResolver {
}
/**
+ * Gets the current name of the service for the given user
+ *
+ * @return either the temporary name (set by
+ * {@link #setTemporaryService(int, String, int)}, or the
+ * {@link #getDefaultServiceName(int) default name}.
+ */
+ @Nullable
+ default String[] getServiceNameList(@UserIdInt int userId) {
+ return getDefaultServiceNameList(userId);
+ }
+
+ /**
* Checks whether the current service is temporary for the given user.
*/
default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
@@ -85,11 +121,11 @@ public interface ServiceNameResolver {
/**
* Temporarily sets the service implementation for the given user.
*
- * @param userId user handle
+ * @param userId user handle
* @param componentName name of the new component
- * @param durationMs how long the change will be valid (the service will be automatically reset
- * to the default component after this timeout expires).
- *
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
* @throws UnsupportedOperationException if not implemented.
*/
default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
@@ -98,10 +134,24 @@ public interface ServiceNameResolver {
}
/**
+ * Temporarily sets the service implementation for the given user.
+ *
+ * @param userId user handle
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ throw new UnsupportedOperationException("temporary user not supported");
+ }
+
+ /**
* Resets the temporary service implementation to the default component for the given user.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default void resetTemporaryService(@UserIdInt int userId) {
@@ -114,11 +164,11 @@ public interface ServiceNameResolver {
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @param userId user handle
+ * @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not
- * set. If the service enabled state is already that value, the command is ignored and this
- * method return {@code false}.
- *
+ * set. If the service enabled state is already that value, the command is
+ * ignored and this
+ * method return {@code false}.
* @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented.
*/
@@ -133,7 +183,6 @@ public interface ServiceNameResolver {
* with the test results.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default boolean isDefaultServiceEnabled(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index dbd3f3529208..d238dae634ad 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -19,6 +19,7 @@ package com.android.server.input;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import android.os.IBinder;
+import android.os.InputConfig;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputMonitor;
@@ -56,18 +57,13 @@ class GestureMonitorSpyWindow {
mWindowHandle.name = name;
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
- mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.visible = true;
- mWindowHandle.focusable = false;
- mWindowHandle.hasWallpaper = false;
- mWindowHandle.paused = false;
mWindowHandle.ownerPid = pid;
mWindowHandle.ownerUid = uid;
- mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.trustedOverlay = true;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
+ mWindowHandle.inputConfig =
+ InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 3c454080ef1c..1a19385e71c5 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -19,6 +19,7 @@ package com.android.server.inputmethod;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import android.annotation.NonNull;
+import android.os.InputConfig;
import android.os.Process;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
@@ -56,20 +57,17 @@ final class HandwritingEventReceiverSurface {
mWindowHandle.name = name;
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
- mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.visible = true;
- mWindowHandle.focusable = false;
- mWindowHandle.hasWallpaper = false;
- mWindowHandle.paused = false;
mWindowHandle.ownerPid = Process.myPid();
mWindowHandle.ownerUid = Process.myUid();
- mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- | WindowManager.LayoutParams.INPUT_FEATURE_INTERCEPTS_STYLUS;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.trustedOverlay = true;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
+ mWindowHandle.inputConfig =
+ InputConfig.NOT_FOCUSABLE
+ | InputConfig.NOT_TOUCHABLE
+ | InputConfig.SPY
+ | InputConfig.INTERCEPTS_STYLUS
+ | InputConfig.TRUSTED_OVERLAY;
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
@@ -85,7 +83,7 @@ final class HandwritingEventReceiverSurface {
void startIntercepting(int imePid, int imeUid) {
mWindowHandle.ownerPid = imePid;
mWindowHandle.ownerUid = imeUid;
- mWindowHandle.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ mWindowHandle.inputConfig &= ~InputConfig.SPY;
new SurfaceControl.Transaction()
.setInputWindowInfo(mInputSurface, mWindowHandle)
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 6cb3b3b6740d..e6fd40902386 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -32,6 +32,7 @@ import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
@@ -108,10 +109,10 @@ final class IInputMethodInvoker {
@AnyThread
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges, boolean stylusHwSupported,
- boolean shouldShowImeSwitcherWhenImeIsShown) {
+ @InputMethodNavButtonFlags int navButtonFlags) {
try {
mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
@@ -147,20 +148,19 @@ final class IInputMethodInvoker {
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
- boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) {
+ boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
try {
mTarget.startInput(startInputToken, inputContext, attribute, restarting,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
}
@AnyThread
- void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown) {
+ void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
try {
- mTarget.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShown);
+ mTarget.onNavButtonFlagsChanged(navButtonFlags);
} catch (RemoteException e) {
logRemoteException(e);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 882e2e397d2c..77dcbd3e9277 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -156,6 +156,7 @@ import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
@@ -335,6 +336,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private final HandwritingModeController mHwController;
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private OverlayableSystemBooleanResourceWrapper mImeDrawsImeNavBarRes;
+
static class SessionState {
final ClientState client;
final IInputMethodInvoker method;
@@ -1723,11 +1728,52 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ private void recreateImeDrawsImeNavBarResIfNecessary(@UserIdInt int targetUserId) {
+ // Currently, com.android.internal.R.bool.config_imeDrawsImeNavBar is overlaid only for the
+ // profile parent user.
+ // TODO(b/221443458): See if we can make OverlayManager be aware of profile groups.
+ final int profileParentUserId = mUserManagerInternal.getProfileParentId(targetUserId);
+ if (mImeDrawsImeNavBarRes != null
+ && mImeDrawsImeNavBarRes.getUserId() != profileParentUserId) {
+ mImeDrawsImeNavBarRes.close();
+ mImeDrawsImeNavBarRes = null;
+ }
+ if (mImeDrawsImeNavBarRes == null) {
+ final Context userContext;
+ if (mContext.getUserId() == profileParentUserId) {
+ userContext = mContext;
+ } else {
+ userContext = mContext.createContextAsUser(UserHandle.of(profileParentUserId),
+ 0 /* flags */);
+ }
+ mImeDrawsImeNavBarRes = OverlayableSystemBooleanResourceWrapper.create(userContext,
+ com.android.internal.R.bool.config_imeDrawsImeNavBar, mHandler, resource -> {
+ synchronized (ImfLock.class) {
+ if (resource == mImeDrawsImeNavBarRes) {
+ sendOnNavButtonFlagsChangedLocked();
+ }
+ }
+ });
+ }
+ }
+
+ @NonNull
+ private static PackageManager getPackageManagerForUser(@NonNull Context context,
+ @UserIdInt int userId) {
+ return context.getUserId() == userId
+ ? context.getPackageManager()
+ : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */)
+ .getPackageManager();
+ }
+
+ @GuardedBy("ImfLock.class")
private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
+ recreateImeDrawsImeNavBarResIfNecessary(newUserId);
+
// ContentObserver should be registered again when the user is changed
mSettingsObserver.registerContentObserverLocked(newUserId);
@@ -1764,9 +1810,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
updateFromSettingsLocked(true);
if (initialUserSwitch) {
- InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
- mSettings.getEnabledInputMethodListLocked(), newUserId,
- mContext.getBasePackageName());
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+ getPackageManagerForUser(mContext, newUserId),
+ mSettings.getEnabledInputMethodListLocked());
}
if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
@@ -1832,6 +1878,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
});
}
+ recreateImeDrawsImeNavBarResIfNecessary(currentUserId);
+
mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
mSettingsObserver.registerContentObserverLocked(currentUserId);
@@ -1853,9 +1901,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
buildInputMethodListLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */);
updateFromSettingsLocked(true);
- InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
- mSettings.getEnabledInputMethodListLocked(), currentUserId,
- mContext.getBasePackageName());
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+ getPackageManagerForUser(mContext, currentUserId),
+ mSettings.getEnabledInputMethodListLocked());
}
}
}
@@ -2412,12 +2460,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
true /* direct */);
}
- final boolean shouldShowImeSwitcherWhenImeIsShown =
- shouldShowImeSwitcherWhenImeIsShownLocked();
+ @InputMethodNavButtonFlags
+ final int navButtonFlags = getInputMethodNavButtonFlagsLocked();
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
- shouldShowImeSwitcherWhenImeIsShown);
+ navButtonFlags);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2681,7 +2729,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ mCurTokenDisplayId);
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
- configChanges, supportStylusHw, shouldShowImeSwitcherWhenImeIsShownLocked());
+ configChanges, supportStylusHw, getInputMethodNavButtonFlagsLocked());
}
@AnyThread
@@ -2938,9 +2986,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean shouldShowImeSwitcherWhenImeIsShownLocked() {
- return shouldShowImeSwitcherLocked(
+ @InputMethodNavButtonFlags
+ private int getInputMethodNavButtonFlagsLocked() {
+ final boolean canImeDrawsImeNavBar =
+ mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get();
+ final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
+ return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
+ | (shouldShowImeSwitcherWhenImeIsShown
+ ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
}
@GuardedBy("ImfLock.class")
@@ -3203,7 +3257,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
}
@GuardedBy("ImfLock.class")
@@ -4680,7 +4734,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
synchronized (ImfLock.class) {
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
}
return true;
case MSG_SYSTEM_UNLOCK_USER: {
@@ -4950,7 +5004,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
- sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ sendOnNavButtonFlagsChangedLocked();
// Notify InputMethodListListeners of the new installed InputMethods.
final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
@@ -4959,14 +5013,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- void sendShouldShowImeSwitcherWhenImeIsShownLocked() {
+ void sendOnNavButtonFlagsChangedLocked() {
final IInputMethodInvoker curMethod = mBindingController.getCurMethod();
if (curMethod == null) {
// No need to send the data if the IME is not yet bound.
return;
}
- curMethod.onShouldShowImeSwitcherWhenImeIsShownChanged(
- shouldShowImeSwitcherWhenImeIsShownLocked());
+ curMethod.onNavButtonFlagsChanged(getInputMethodNavButtonFlagsLocked());
}
@GuardedBy("ImfLock.class")
@@ -6078,10 +6131,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
setInputMethodEnabledLocked(imi.getId(), true);
}
updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
- InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
- mSettings.getEnabledInputMethodListLocked(),
- mSettings.getCurrentUserId(),
- mContext.getBasePackageName());
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+ getPackageManagerForUser(mContext, mSettings.getCurrentUserId()),
+ mSettings.getEnabledInputMethodListLocked());
nextIme = mSettings.getSelectedInputMethod();
nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
} else {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 98bde11ad517..c255fe14c03e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -203,7 +203,7 @@ final class InputMethodMenuController {
attrs.setTitle("Select input method");
w.setAttributes(attrs);
mService.updateSystemUiLocked();
- mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ mService.sendOnNavButtonFlagsChangedLocked();
mSwitchingDialog.show();
}
}
@@ -239,7 +239,7 @@ final class InputMethodMenuController {
mSwitchingDialogTitleView = null;
mService.updateSystemUiLocked();
- mService.sendShouldShowImeSwitcherWhenImeIsShownLocked();
+ mService.sendOnNavButtonFlagsChangedLocked();
mDialogBuilder = null;
mIms = null;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index e619fff24d22..4633df23516d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -18,17 +18,16 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -663,8 +662,9 @@ final class InputMethodUtils {
return !subtype.isAuxiliary();
}
- static void setNonSelectedSystemImesDisabledUntilUsed(IPackageManager packageManager,
- List<InputMethodInfo> enabledImis, @UserIdInt int userId, String callingPackage) {
+ @UserHandleAware
+ static void setNonSelectedSystemImesDisabledUntilUsed(PackageManager packageManagerForUser,
+ List<InputMethodInfo> enabledImis) {
if (DEBUG) {
Slog.d(TAG, "setNonSelectedSystemImesDisabledUntilUsed");
}
@@ -675,7 +675,8 @@ final class InputMethodUtils {
}
// Only the current spell checker should be treated as an enabled one.
final SpellCheckerInfo currentSpellChecker =
- TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId);
+ TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(
+ packageManagerForUser.getUserId());
for (final String packageName : systemImesDisabledUntilUsed) {
if (DEBUG) {
Slog.d(TAG, "check " + packageName);
@@ -702,11 +703,12 @@ final class InputMethodUtils {
}
ApplicationInfo ai = null;
try {
- ai = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
- } catch (RemoteException e) {
+ ai = packageManagerForUser.getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
+ } catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "getApplicationInfo failed. packageName=" + packageName
- + " userId=" + userId, e);
+ + " userId=" + packageManagerForUser.getUserId(), e);
continue;
}
if (ai == null) {
@@ -717,18 +719,18 @@ final class InputMethodUtils {
if (!isSystemPackage) {
continue;
}
- setDisabledUntilUsed(packageManager, packageName, userId, callingPackage);
+ setDisabledUntilUsed(packageManagerForUser, packageName);
}
}
- private static void setDisabledUntilUsed(IPackageManager packageManager, String packageName,
- int userId, String callingPackage) {
+ private static void setDisabledUntilUsed(PackageManager packageManagerForUser,
+ String packageName) {
final int state;
try {
- state = packageManager.getApplicationEnabledSetting(packageName, userId);
- } catch (RemoteException e) {
+ state = packageManagerForUser.getApplicationEnabledSetting(packageName);
+ } catch (IllegalArgumentException e) {
Slog.w(TAG, "getApplicationEnabledSetting failed. packageName=" + packageName
- + " userId=" + userId, e);
+ + " userId=" + packageManagerForUser.getUserId(), e);
return;
}
if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
@@ -737,12 +739,12 @@ final class InputMethodUtils {
Slog.d(TAG, "Update state(" + packageName + "): DISABLED_UNTIL_USED");
}
try {
- packageManager.setApplicationEnabledSetting(packageName,
+ packageManagerForUser.setApplicationEnabledSetting(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- 0 /* newState */, userId, callingPackage);
- } catch (RemoteException e) {
+ 0 /* newState */);
+ } catch (IllegalArgumentException e) {
Slog.w(TAG, "setApplicationEnabledSetting failed. packageName=" + packageName
- + " userId=" + userId + " callingPackage=" + callingPackage, e);
+ + " userId=" + packageManagerForUser.getUserId(), e);
return;
}
} else {
diff --git a/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java b/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java
new file mode 100644
index 000000000000..33e7a7621340
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/OverlayableSystemBooleanResourceWrapper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
+
+import android.annotation.AnyThread;
+import android.annotation.BoolRes;
+import android.annotation.NonNull;
+import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+/**
+ * A wrapper object for any boolean resource defined in {@code "android"} package, in a way that is
+ * aware of per-user Runtime Resource Overlay (RRO).
+ */
+final class OverlayableSystemBooleanResourceWrapper implements AutoCloseable {
+ private static final String TAG = "OverlayableSystemBooleanResourceWrapper";
+
+ private static final String SYSTEM_PACKAGE_NAME = "android";
+
+ @UserIdInt
+ private final int mUserId;
+ @NonNull
+ private final AtomicBoolean mValueRef;
+ @NonNull
+ private final AtomicReference<Runnable> mCleanerRef;
+
+ /**
+ * Creates {@link OverlayableSystemBooleanResourceWrapper} for the given boolean resource ID
+ * with a value change callback for the user associated with the {@link Context}.
+ *
+ * @param userContext The {@link Context} to be used to access the resource. This needs to be
+ * associated with the right user because the Runtime Resource Overlay (RRO)
+ * is per-user configuration.
+ * @param boolResId The resource ID to be queried.
+ * @param handler {@link Handler} to be used to dispatch {@code callback}.
+ * @param callback The callback to be notified when the specified value might be updated.
+ * The callback needs to take care of spurious wakeup. The value returned from
+ * {@link #get()} may look to be exactly the same as the previously read value
+ * e.g. when the value is changed from {@code false} to {@code true} to
+ * {@code false} in a very short period of time, because {@link #get()} always
+ * does volatile-read.
+ * @return New {@link OverlayableSystemBooleanResourceWrapper}.
+ */
+ @NonNull
+ @UserHandleAware
+ static OverlayableSystemBooleanResourceWrapper create(@NonNull Context userContext,
+ @BoolRes int boolResId, @NonNull Handler handler,
+ @NonNull Consumer<OverlayableSystemBooleanResourceWrapper> callback) {
+
+ // Note that we cannot fully trust this initial value due to the dead time between obtaining
+ // the value here and setting up a broadcast receiver for change callback below.
+ // We will refresh the value again later after setting up the change callback anyway.
+ final AtomicBoolean valueRef = new AtomicBoolean(evaluate(userContext, boolResId));
+
+ final AtomicReference<Runnable> cleanerRef = new AtomicReference<>();
+
+ final OverlayableSystemBooleanResourceWrapper object =
+ new OverlayableSystemBooleanResourceWrapper(userContext.getUserId(), valueRef,
+ cleanerRef);
+
+ final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
+ intentFilter.addDataSchemeSpecificPart(SYSTEM_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL);
+
+ final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final boolean newValue = evaluate(userContext, boolResId);
+ if (newValue != valueRef.getAndSet(newValue)) {
+ callback.accept(object);
+ }
+ }
+ };
+ userContext.registerReceiver(broadcastReceiver, intentFilter,
+ null /* broadcastPermission */, handler,
+ Context.RECEIVER_NOT_EXPORTED);
+ cleanerRef.set(() -> userContext.unregisterReceiver(broadcastReceiver));
+
+ // Make sure that the initial observable value is obtained after the change callback is set.
+ valueRef.set(evaluate(userContext, boolResId));
+ return object;
+ }
+
+ private OverlayableSystemBooleanResourceWrapper(@UserIdInt int userId,
+ @NonNull AtomicBoolean valueRef, @NonNull AtomicReference<Runnable> cleanerRef) {
+ mUserId = userId;
+ mValueRef = valueRef;
+ mCleanerRef = cleanerRef;
+ }
+
+ /**
+ * @return The boolean resource value.
+ */
+ @AnyThread
+ boolean get() {
+ return mValueRef.get();
+ }
+
+ /**
+ * @return The user ID associated with this resource reader.
+ */
+ @AnyThread
+ @UserIdInt
+ int getUserId() {
+ return mUserId;
+ }
+
+ @AnyThread
+ private static boolean evaluate(@NonNull Context context, @BoolRes int boolResId) {
+ try {
+ return context.getPackageManager()
+ .getResourcesForApplication(SYSTEM_PACKAGE_NAME)
+ .getBoolean(boolResId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "getResourcesForApplication(\"" + SYSTEM_PACKAGE_NAME + "\") failed", e);
+ return false;
+ }
+ }
+
+ /**
+ * Cleans up the callback.
+ */
+ @AnyThread
+ @Override
+ public void close() {
+ final Runnable cleaner = mCleanerRef.getAndSet(null);
+ if (cleaner != null) {
+ cleaner.run();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
deleted file mode 100644
index 6b442a6a395e..000000000000
--- a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
+++ /dev/null
@@ -1,130 +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.server.logcat;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.ServiceManager;
-import android.os.logcat.ILogcatManagerService;
-import android.util.Slog;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-
-
-/**
- * This dialog is shown to the user before an activity in a harmful app is launched.
- *
- * See {@code PackageManager.setLogcatAppInfo} for more info.
- */
-public class LogAccessConfirmationActivity extends AlertActivity implements
- DialogInterface.OnClickListener {
- private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName();
-
- private String mPackageName;
- private IntentSender mTarget;
- private final ILogcatManagerService mLogcatManagerService =
- ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
-
- private int mUid;
- private int mGid;
- private int mPid;
- private int mFd;
-
- private static final String EXTRA_UID = "uid";
- private static final String EXTRA_GID = "gid";
- private static final String EXTRA_PID = "pid";
- private static final String EXTRA_FD = "fd";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Intent intent = getIntent();
- mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- mUid = intent.getIntExtra("uid", 0);
- mGid = intent.getIntExtra("gid", 0);
- mPid = intent.getIntExtra("pid", 0);
- mFd = intent.getIntExtra("fd", 0);
-
- final AlertController.AlertParams p = mAlertParams;
- p.mTitle = getString(R.string.log_access_confirmation_title);
- p.mView = createView();
-
- p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow);
- p.mPositiveButtonListener = this;
- p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny);
- p.mNegativeButtonListener = this;
-
- mAlert.installContent(mAlertParams);
- }
-
- private View createView() {
- final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
- null /*root*/);
- ((TextView) view.findViewById(R.id.app_name_text))
- .setText(mPackageName);
- ((TextView) view.findViewById(R.id.message))
- .setText(getIntent().getExtras().getString("body"));
- return view;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
- try {
- mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
- } catch (Throwable t) {
- Slog.e(TAG, "Could not start the LogcatManagerService.", t);
- }
- finish();
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- try {
- mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
- } catch (Throwable t) {
- Slog.e(TAG, "Could not start the LogcatManagerService.", t);
- }
- finish();
- break;
- }
- }
-
- /**
- * Create the Intent for a LogAccessConfirmationActivity.
- */
- public static Intent createIntent(Context context, String targetPackageName,
- IntentSender target, int uid, int gid, int pid, int fd) {
- final Intent intent = new Intent();
- intent.setClass(context, LogAccessConfirmationActivity.class);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
- intent.putExtra(EXTRA_UID, uid);
- intent.putExtra(EXTRA_GID, gid);
- intent.putExtra(EXTRA_PID, pid);
- intent.putExtra(EXTRA_FD, fd);
-
- return intent;
- }
-
-}
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
new file mode 100644
index 000000000000..7116ca30c5a9
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.logcat;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.logcat.ILogcatManagerService;
+import android.util.Slog;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * Dialog responsible for obtaining user consent per-use log access
+ */
+public class LogAccessDialogActivity extends Activity implements
+ View.OnClickListener {
+ private static final String TAG = LogAccessDialogActivity.class.getSimpleName();
+ private Context mContext;
+
+ private final ILogcatManagerService mLogcatManagerService =
+ ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
+
+ private String mPackageName;
+
+ private int mUid;
+ private int mGid;
+ private int mPid;
+ private int mFd;
+ private String mAlertTitle;
+ private AlertDialog.Builder mAlertDialog;
+ private AlertDialog mAlert;
+
+ private static final int DIALOG_TIME_OUT = 300000;
+ private static final int MSG_DISMISS_DIALOG = 0;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = this;
+
+ Intent intent = getIntent();
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
+ mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
+ mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
+ mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
+ mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+
+ if (mAlertTitle != null) {
+
+ mAlertDialog = new AlertDialog.Builder(this);
+ mAlertDialog.setView(createView());
+
+ mAlert = mAlertDialog.create();
+ mAlert.show();
+ mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT);
+
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mAlert != null && mAlert.isShowing()) {
+ mAlert.dismiss();
+ }
+ mAlert = null;
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_DISMISS_DIALOG:
+ if (mAlert != null) {
+ mAlert.dismiss();
+ mAlert = null;
+ try {
+ mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Fails to call remote functions", e);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ private String getTitleString(Context context, String callingPackage, int uid) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(
+ com.android.internal.R.string.log_access_confirmation_title,
+ pm.getApplicationInfoAsUser(callingPackage,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ UserHandle.getUserId(uid)).loadLabel(pm));
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "App name is unknown.", e);
+ return null;
+ }
+ }
+
+ private View createView() {
+ final View view = getLayoutInflater().inflate(
+ R.layout.log_access_user_consent_dialog_permission, null /*root*/);
+
+ ((TextView) view.findViewById(R.id.log_access_dialog_title))
+ .setText(mAlertTitle);
+
+ Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button);
+ button_allow.setOnClickListener(this);
+
+ Button button_deny = (Button) view.findViewById(R.id.log_access_dialog_deny_button);
+ button_deny.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.log_access_dialog_allow_button:
+ try {
+ mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Fails to call remote functions", e);
+ }
+ finish();
+ break;
+ case R.id.log_access_dialog_deny_button:
+ try {
+ mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Fails to call remote functions", e);
+ }
+ finish();
+ break;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 490e00e70f26..7b63fa24088b 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -18,15 +18,12 @@ package com.android.server.logcat;
import android.annotation.NonNull;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManagerInternal;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
import android.os.ILogd;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -34,16 +31,14 @@ import android.os.UserHandle;
import android.os.logcat.ILogcatManagerService;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+
/**
* Service responsible for managing the access to Logcat.
*/
@@ -54,43 +49,16 @@ public final class LogcatManagerService extends SystemService {
private final BinderService mBinderService;
private final ExecutorService mThreadExecutor;
private ILogd mLogdService;
- private NotificationManager mNotificationManager;
private @NonNull ActivityManager mActivityManager;
private ActivityManagerInternal mActivityManagerInternal;
private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2;
- private static int sUidImportanceListenerCount = 0;
- private static final int AID_SHELL_UID = 2000;
-
- // TODO This allowlist is just a temporary workaround for the tests:
- // FrameworksServicesTests
- // PlatformRuleTests
- // After adapting the test suites, the allowlist will be removed in
- // the upcoming bug fix patches.
- private static final String[] ALLOWABLE_TESTING_PACKAGES = {
- "android.platform.test.rule.tests",
- "com.android.frameworks.servicestests"
- };
-
- // TODO Same as the above ALLOWABLE_TESTING_PACKAGES.
- private boolean isAllowableTestingPackage(int uid) {
- PackageManager pm = mContext.getPackageManager();
-
- String[] packageNames = pm.getPackagesForUid(uid);
-
- if (ArrayUtils.isEmpty(packageNames)) {
- return false;
- }
-
- for (String name : packageNames) {
- Slog.e(TAG, "isAllowableTestingPackage: " + name);
-
- if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) {
- return true;
- }
- }
-
- return false;
- };
+ private static final String TARGET_PACKAGE_NAME = "android";
+ private static final String TARGET_ACTIVITY_NAME =
+ "com.android.server.logcat.LogAccessDialogActivity";
+ private static final String EXTRA_UID = "com.android.server.logcat.uid";
+ private static final String EXTRA_GID = "com.android.server.logcat.gid";
+ private static final String EXTRA_PID = "com.android.server.logcat.pid";
+ private static final String EXTRA_FD = "com.android.server.logcat.fd";
private final class BinderService extends ILogcatManagerService.Stub {
@Override
@@ -110,7 +78,7 @@ public final class LogcatManagerService extends SystemService {
try {
getLogdService().approve(uid, gid, pid, fd);
} catch (RemoteException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Fails to call remote functions", e);
}
}
@@ -119,7 +87,7 @@ public final class LogcatManagerService extends SystemService {
try {
getLogdService().decline(uid, gid, pid, fd);
} catch (RemoteException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Fails to call remote functions", e);
}
}
}
@@ -133,46 +101,16 @@ public final class LogcatManagerService extends SystemService {
}
}
- private String getBodyString(Context context, String callingPackage, int uid) {
- PackageManager pm = context.getPackageManager();
- try {
- return context.getString(
- com.android.internal.R.string.log_access_confirmation_body,
- pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO,
- UserHandle.getUserId(uid)).loadLabel(pm));
- } catch (NameNotFoundException e) {
- // App name is unknown.
- return null;
- }
- }
-
- private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid,
- int fd) {
-
+ private void showDialog(int uid, int gid, int pid, int fd) {
final ActivityManagerInternal activityManagerInternal =
LocalServices.getService(ActivityManagerInternal.class);
PackageManager pm = mContext.getPackageManager();
String packageName = activityManagerInternal.getPackageNameByPid(pid);
if (packageName != null) {
- String notificationBody = getBodyString(mContext, packageName, uid);
-
- final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
- packageName, null, uid, gid, pid, fd);
-
- if (notificationBody == null) {
- // Decline the logd access if the nofitication body is unknown
- Slog.e(TAG, "Unknown notification body, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- // TODO Next version will replace notification with dialogue
- // per UX guidance.
- generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody,
- mIntent);
+ Intent mIntent = createIntent(packageName, uid, gid, pid, fd);
+ mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
return;
-
}
String[] packageNames = pm.getPackagesForUid(uid);
@@ -186,115 +124,28 @@ public final class LogcatManagerService extends SystemService {
String firstPackageName = packageNames[0];
- if (firstPackageName == null || firstPackageName.length() == 0) {
+ if (firstPackageName.isEmpty() || firstPackageName == null) {
// Decline the logd access if the package name from uid is unknown
Slog.e(TAG, "Unknown calling package name, declining the logd access");
declineLogdAccess(uid, gid, pid, fd);
return;
}
- String notificationBody = getBodyString(mContext, firstPackageName, uid);
-
- final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
- firstPackageName, null, uid, gid, pid, fd);
-
- if (notificationBody == null) {
- Slog.e(TAG, "Unknown notification body, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- // TODO Next version will replace notification with dialogue
- // per UX guidance.
- generateNotificationWithBodyContent(notificationId, clientInfo,
- notificationBody, mIntent);
+ final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd);
+ mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
}
private void declineLogdAccess(int uid, int gid, int pid, int fd) {
try {
getLogdService().decline(uid, gid, pid, fd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
- }
- }
-
- private void generateNotificationWithBodyContent(int notificationId, String clientInfo,
- String notificationBody, Intent intent) {
- final Notification.Builder notificationBuilder = new Notification.Builder(
- mContext,
- SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.setIdentifier(String.valueOf(notificationId) + clientInfo);
- intent.putExtra("body", notificationBody);
-
- notificationBuilder
- .setSmallIcon(R.drawable.ic_info)
- .setContentTitle(
- mContext.getString(R.string.log_access_confirmation_title))
- .setContentText(notificationBody)
- .setContentIntent(
- PendingIntent.getActivity(mContext, 0, intent,
- PendingIntent.FLAG_IMMUTABLE))
- .setTicker(mContext.getString(R.string.log_access_confirmation_title))
- .setOnlyAlertOnce(true)
- .setAutoCancel(true);
- mNotificationManager.notify(notificationId, notificationBuilder.build());
- }
-
- /**
- * A class which watches an uid for background access and notifies the logdMonitor when
- * the package status becomes foreground (importance change)
- */
- private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
- private final int mExpectedUid;
- private final int mExpectedGid;
- private final int mExpectedPid;
- private final int mExpectedFd;
- private int mExpectedImportance;
- private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
-
- UidImportanceListener(int uid, int gid, int pid, int fd, int importance) {
- mExpectedUid = uid;
- mExpectedGid = gid;
- mExpectedPid = pid;
- mExpectedFd = fd;
- mExpectedImportance = importance;
- }
-
- @Override
- public void onUidImportance(int uid, int importance) {
- if (uid == mExpectedUid) {
- mCurrentImportance = importance;
-
- /**
- * 1) If the process status changes to foreground, send a notification
- * for user consent.
- * 2) If the process status remains background, we decline logd access request.
- **/
- if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
- String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd);
- sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid,
- mExpectedFd);
- mActivityManager.removeOnUidImportanceListener(this);
-
- synchronized (LogcatManagerService.this) {
- sUidImportanceListenerCount--;
- }
- } else {
- try {
- getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
- }
- }
- }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Fails to call remote functions", e);
}
}
private static String getClientInfo(int uid, int gid, int pid, int fd) {
return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID="
- + Integer.toString(pid) + " FD=" + Integer.toString(fd);
+ + Integer.toString(pid) + " FD=" + Integer.toString(fd);
}
private class LogdMonitor implements Runnable {
@@ -338,18 +189,22 @@ public final class LogcatManagerService extends SystemService {
try {
getLogdService().approve(mUid, mGid, mPid, mFd);
} catch (RemoteException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Fails to call remote functions", e);
}
return;
}
- // TODO Temporarily approve all the requests to unblock testing failures.
- try {
- getLogdService().approve(mUid, mGid, mPid, mFd);
- } catch (RemoteException e) {
- e.printStackTrace();
+ final int procState = mActivityManager.getUidImportance(Binder.getCallingUid());
+ // If the process is foreground, send a notification for user consent
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ showDialog(mUid, mGid, mPid, mFd);
+ } else {
+ /**
+ * If the process is background, decline the logd access.
+ **/
+ declineLogdAccess(mUid, mGid, mPid, mFd);
+ return;
}
- return;
}
}
}
@@ -360,7 +215,6 @@ public final class LogcatManagerService extends SystemService {
mBinderService = new BinderService();
mThreadExecutor = Executors.newCachedThreadPool();
mActivityManager = context.getSystemService(ActivityManager.class);
- mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -375,4 +229,23 @@ public final class LogcatManagerService extends SystemService {
private void addLogdService() {
mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
}
+
+ /**
+ * Create the Intent for LogAccessDialogActivity.
+ */
+ public Intent createIntent(String targetPackageName, int uid, int gid, int pid, int fd) {
+ final Intent intent = new Intent();
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ intent.putExtra(EXTRA_UID, uid);
+ intent.putExtra(EXTRA_GID, gid);
+ intent.putExtra(EXTRA_PID, pid);
+ intent.putExtra(EXTRA_FD, fd);
+
+ intent.setComponent(new ComponentName(TARGET_PACKAGE_NAME, TARGET_ACTIVITY_NAME));
+
+ return intent;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c4731aa7b522..265ad7dee388 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1623,18 +1623,6 @@ public class NotificationManagerService extends SystemService {
}
};
- @VisibleForTesting
- final IAppOpsCallback mAppOpsCallback = new IAppOpsCallback.Stub() {
- @Override public void opChanged(int op, int uid, String packageName) {
- if (mEnableAppSettingMigration) {
- int opValue = mAppOps.checkOpNoThrow(
- AppOpsManager.OP_POST_NOTIFICATION, uid, packageName);
- boolean blocked = op != MODE_ALLOWED;
- sendAppBlockStateChangedBroadcast(packageName, uid, blocked);
- }
- }
- };
-
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2133,12 +2121,6 @@ public class NotificationManagerService extends SystemService {
mUsageStatsManagerInternal = usageStatsManagerInternal;
mAppOps = appOps;
mAppOpsService = iAppOps;
- try {
- mAppOpsService.startWatchingMode(
- AppOpsManager.OP_POST_NOTIFICATION, null, mAppOpsCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register OP_POST_NOTIFICATION listener");
- }
mAppUsageStats = appUsageStats;
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mCompanionManager = companionManager;
@@ -3409,6 +3391,7 @@ public class NotificationManagerService extends SystemService {
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
} else {
synchronized (mNotificationLock) {
boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid)
@@ -3746,6 +3729,10 @@ public class NotificationManagerService extends SystemService {
if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
&& startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
hasRequestedNotificationPermission = true;
+ if (mPermissionPolicyInternal == null) {
+ mPermissionPolicyInternal =
+ LocalServices.getService(PermissionPolicyInternal.class);
+ }
mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
UserHandle.getUserId(uid), startingTaskId,
mPermissionPolicyInternal));
@@ -3764,19 +3751,7 @@ public class NotificationManagerService extends SystemService {
try {
int uid = mPackageManager.getPackageUid(pkg, 0,
UserHandle.getUserId(Binder.getCallingUid()));
- List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid);
- for (int i = 0; i < tasks.size(); i++) {
- ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo();
- if (mPermissionPolicyInternal == null) {
- mPermissionPolicyInternal =
- LocalServices.getService(PermissionPolicyInternal.class);
- }
- if (mPermissionPolicyInternal != null
- && mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) {
- taskId = task.taskId;
- break;
- }
- }
+ taskId = mAtm.getTaskToShowPermissionDialogOn(pkg, uid);
} catch (RemoteException e) {
// Do nothing
}
@@ -4068,13 +4043,12 @@ public class NotificationManagerService extends SystemService {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
- String pkg, int userId) {
+ String pkg, int uid) {
checkCallerIsSystem();
- if (!areNotificationsEnabledForPackage(pkg,
- mPackageManagerInternal.getPackageUid(pkg, 0, userId))) {
+ if (!areNotificationsEnabledForPackage(pkg, uid)) {
return ParceledListSlice.emptyList();
}
- return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
+ return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, uid);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 6b9e374771ff..e551f1056b24 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -35,6 +35,7 @@ import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.util.Collections;
@@ -178,13 +179,16 @@ public final class PermissionHelper {
boolean userSet, boolean reviewRequired) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
- // Do not change fixed permissions, and do not change non-user set permissions that are
- // granted by default, or granted by role.
- if (isPermissionFixed(packageName, userId)
- || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
- return;
- }
try {
+ // Do not change the permission if the package doesn't request it, do not change fixed
+ // permissions, and do not change non-user set permissions that are granted by default,
+ // or granted by role.
+ if (!packageRequestsNotificationPermission(packageName, userId)
+ || isPermissionFixed(packageName, userId)
+ || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
+ return;
+ }
+
boolean currentlyGranted = mPmi.checkPermission(packageName, NOTIFICATION_PERMISSION,
userId) != PackageManager.PERMISSION_DENIED;
if (grant && !reviewRequired && !currentlyGranted) {
@@ -278,6 +282,19 @@ public final class PermissionHelper {
}
}
+ private boolean packageRequestsNotificationPermission(String packageName,
+ @UserIdInt int userId) {
+ assertFlag();
+ try {
+ String[] permissions = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS,
+ userId).requestedPermissions;
+ return ArrayUtils.contains(permissions, NOTIFICATION_PERMISSION);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ }
+
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 11858904a69a..1f7d65e6c604 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1669,14 +1669,14 @@ public class PreferencesHelper implements RankingConfig {
}
/**
- * Gets all notification channels associated with the given pkg and userId that can bypass dnd
+ * Gets all notification channels associated with the given pkg and uid that can bypass dnd
*/
public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
- int userId) {
+ int uid) {
List<NotificationChannel> channels = new ArrayList<>();
synchronized (mPackagePreferences) {
final PackagePreferences r = mPackagePreferences.get(
- packagePreferencesKey(pkg, userId));
+ packagePreferencesKey(pkg, uid));
if (r != null) {
for (NotificationChannel channel : r.channels.values()) {
if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 2a6dd8410acb..b0d40efed690 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -112,7 +112,7 @@ public class ZenModeFiltering {
}
if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
if (consolidatedPolicy.allowRepeatCallers()
- && REPEAT_CALLERS.isRepeat(context, extras)) {
+ && REPEAT_CALLERS.isRepeat(context, extras, null)) {
ZenLog.traceMatchesCallFilter(true, "repeat caller");
return true;
}
@@ -229,7 +229,8 @@ public class ZenModeFiltering {
}
if (isCall(record)) {
if (policy.allowRepeatCallers()
- && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
+ && REPEAT_CALLERS.isRepeat(
+ mContext, extras(record), record.getPhoneNumbers())) {
ZenLog.traceNotIntercepted(record, "repeatCaller");
return false;
}
@@ -350,6 +351,9 @@ public class ZenModeFiltering {
private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>();
private int mThresholdMinutes;
+ // Record all people URIs in the extras bundle as well as the provided phoneNumbers set
+ // as callers. The phoneNumbers set is used to pass in any additional phone numbers
+ // associated with the people URIs as separately retrieved from contacts.
private synchronized void recordCall(Context context, Bundle extras,
ArraySet<String> phoneNumbers) {
setThresholdMinutes(context);
@@ -362,7 +366,13 @@ public class ZenModeFiltering {
recordCallers(extraPeople, phoneNumbers, now);
}
- private synchronized boolean isRepeat(Context context, Bundle extras) {
+ // Determine whether any people in the provided extras bundle or phone number set is
+ // a repeat caller. The extras bundle contains the people associated with a specific
+ // notification, and will suffice for most callers; the phoneNumbers array may be used
+ // to additionally check any specific phone numbers previously retrieved from contacts
+ // associated with the people in the extras bundle.
+ private synchronized boolean isRepeat(Context context, Bundle extras,
+ ArraySet<String> phoneNumbers) {
setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return false;
final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
@@ -370,7 +380,7 @@ public class ZenModeFiltering {
final long now = System.currentTimeMillis();
cleanUp(mTelCalls, now);
cleanUp(mOtherCalls, now);
- return checkCallers(context, extraPeople);
+ return checkCallers(context, extraPeople, phoneNumbers);
}
private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
@@ -433,7 +443,31 @@ public class ZenModeFiltering {
}
}
- private synchronized boolean checkCallers(Context context, String[] people) {
+ // helper function to check mTelCalls array for a number, and also check its decoded
+ // version
+ private synchronized boolean checkForNumber(String number, String defaultCountryCode) {
+ if (mTelCalls.containsKey(number)) {
+ // check directly via map first
+ return true;
+ } else {
+ // see if a number that matches via areSameNumber exists
+ String numberToCheck = Uri.decode(number);
+ if (numberToCheck != null) {
+ for (String prev : mTelCalls.keySet()) {
+ if (PhoneNumberUtils.areSamePhoneNumber(
+ numberToCheck, prev, defaultCountryCode)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // Check whether anyone in the provided array of people URIs or phone number set matches a
+ // previously recorded phone call.
+ private synchronized boolean checkCallers(Context context, String[] people,
+ ArraySet<String> phoneNumbers) {
// get the default country code for checking telephone numbers
final String defaultCountryCode =
context.getSystemService(TelephonyManager.class).getNetworkCountryIso();
@@ -443,20 +477,8 @@ public class ZenModeFiltering {
final Uri uri = Uri.parse(person);
if ("tel".equals(uri.getScheme())) {
String number = uri.getSchemeSpecificPart();
- if (mTelCalls.containsKey(number)) {
- // check directly via map first
+ if (checkForNumber(number, defaultCountryCode)) {
return true;
- } else {
- // see if a number that matches via areSameNumber exists
- String numberToCheck = Uri.decode(number);
- if (numberToCheck != null) {
- for (String prev : mTelCalls.keySet()) {
- if (PhoneNumberUtils.areSamePhoneNumber(
- numberToCheck, prev, defaultCountryCode)) {
- return true;
- }
- }
- }
}
} else {
if (mOtherCalls.containsKey(person)) {
@@ -464,6 +486,17 @@ public class ZenModeFiltering {
}
}
}
+
+ // also check any passed-in phone numbers
+ if (phoneNumbers != null) {
+ for (String num : phoneNumbers) {
+ if (checkForNumber(num, defaultCountryCode)) {
+ return true;
+ }
+ }
+ }
+
+ // no matches
return false;
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 6a2b2d582458..76d3d233d49a 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -29,7 +29,6 @@ import android.apex.CompressedApexInfoList;
import android.apex.IApexService;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.result.ParseResult;
@@ -834,7 +833,7 @@ public abstract class ApexManager {
throw new RuntimeException(re);
} catch (Exception e) {
throw new PackageManagerException(
- PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"apexd verification failed : " + e.getMessage());
}
}
@@ -861,7 +860,7 @@ public abstract class ApexManager {
throw new RuntimeException(re);
} catch (Exception e) {
throw new PackageManagerException(
- PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to mark apexd session as ready : " + e.getMessage());
}
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index c24bfec5fb43..5013570fa6b8 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -36,7 +36,6 @@ import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.security.AndroidKeyStoreMaintenance;
import android.system.keystore2.Domain;
-import android.system.keystore2.KeyDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -555,26 +554,6 @@ final class AppDataHelper {
return prepareAppDataFuture;
}
- public void migrateKeyStoreData(int previousAppId, int appId) {
- // If previous UID is system UID, declaring inheritKeyStoreKeys is not supported.
- // Silently ignore the request to migrate keys.
- if (previousAppId == Process.SYSTEM_UID) return;
-
- for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) {
- int srcUid = UserHandle.getUid(userId, previousAppId);
- int destUid = UserHandle.getUid(userId, appId);
- final KeyDescriptor[] keys = AndroidKeyStoreMaintenance.listEntries(Domain.APP, srcUid);
- if (keys == null) continue;
- for (final KeyDescriptor key : keys) {
- KeyDescriptor dest = new KeyDescriptor();
- dest.domain = Domain.APP;
- dest.nspace = destUid;
- dest.alias = key.alias;
- AndroidKeyStoreMaintenance.migrateKeyNamespace(key, dest);
- }
- }
- }
-
void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
return;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 31df0a53eaa9..ecbb4a9d45a1 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -557,7 +557,7 @@ public final class BackgroundDexOptService {
/** Gets the size of a package. */
private long getPackageSize(PackageManagerService pm, String pkg) {
- PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ PackageInfo info = pm.snapshotComputer().getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
long size = 0;
if (info != null && info.applicationInfo != null) {
File path = Paths.get(info.applicationInfo.sourceDir).toFile();
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index df7387dc946c..f1394d403bf7 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -119,14 +119,10 @@ public final class BroadcastHelper {
intent.setPackage(targetPkg);
}
// Modify the UID when posting to other users
- final String[] uidExtraNames =
- { Intent.EXTRA_UID, Intent.EXTRA_PREVIOUS_UID, Intent.EXTRA_NEW_UID };
- for (String name : uidExtraNames) {
- int uid = intent.getIntExtra(name, -1);
- if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
- uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
- intent.putExtra(name, uid);
- }
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
+ uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ intent.putExtra(Intent.EXTRA_UID, uid);
}
if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST,
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 2a4882ac7d4f..6103d688e2b1 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -284,6 +284,21 @@ public interface Computer extends PackageDataSnapshot {
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
long updateFlagsForResolve(long flags, int userId, int callingUid, boolean wantInstantApps,
boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+
+ /**
+ * Checks if the request is from the system or an app that has the appropriate cross-user
+ * permissions defined as follows:
+ * <ul>
+ * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+ * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group
+ * to the caller.</li>
+ * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile
+ * group as the caller.</li>
+ * </ul>
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 87a83effee1a..664c7bb929d7 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -243,9 +243,7 @@ final class DeletePackageHelper {
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem);
- if (disabledSystemPs != null) {
- info.sendSystemPackageUpdatedBroadcasts(disabledSystemPs.getAppId());
- }
+ info.sendSystemPackageUpdatedBroadcasts();
}
// Force a gc to clear up things.
@@ -603,9 +601,6 @@ final class DeletePackageHelper {
if (outInfo != null) {
// Delete the updated package
outInfo.mIsRemovedPackageSystemUpdate = true;
- if (disabledPs.getAppId() != deletedPs.getAppId()) {
- outInfo.mNewAppId = disabledPs.getAppId();
- }
}
if (disabledPs.getVersionCode() < deletedPs.getVersionCode()
@@ -685,7 +680,8 @@ final class DeletePackageHelper {
return;
}
- if (!deleteAllUsers && mPm.getBlockUninstallForUser(internalPackageName, userId)) {
+ if (!deleteAllUsers && mPm.mIPackageManager
+ .getBlockUninstallForUser(internalPackageName, userId)) {
mPm.mHandler.post(() -> {
try {
observer.onPackageDeleted(packageName,
@@ -705,11 +701,14 @@ final class DeletePackageHelper {
// Queue up an async operation since the package deletion may take a little while.
mPm.mHandler.post(() -> {
int returnCode;
- final PackageSetting ps = mPm.mSettings.getPackageLPr(internalPackageName);
+ final Computer innerSnapshot = mPm.snapshotComputer();
+ final PackageStateInternal packageState =
+ innerSnapshot.getPackageStateInternal(internalPackageName);
boolean doDeletePackage = true;
- if (ps != null) {
+ if (packageState != null) {
final boolean targetIsInstantApp =
- ps.getInstantApp(UserHandle.getUserId(callingUid));
+ packageState.getUserStateOrDefault(UserHandle.getUserId(callingUid))
+ .isInstantApp();
doDeletePackage = !targetIsInstantApp
|| canViewInstantApps;
}
@@ -718,7 +717,7 @@ final class DeletePackageHelper {
returnCode = deletePackageX(internalPackageName, versionCode,
userId, deleteFlags, false /*removedBySystem*/);
} else {
- int[] blockUninstallUserIds = getBlockUninstallForUsers(
+ int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
internalPackageName, users);
// If nobody is blocking uninstall, proceed with delete for all users
if (ArrayUtils.isEmpty(blockUninstallUserIds)) {
@@ -769,39 +768,40 @@ final class DeletePackageHelper {
}
final int callingUserId = UserHandle.getUserId(callingUid);
// If the caller installed the pkgName, then allow it to silently uninstall.
- if (callingUid == mPm.getPackageUid(
- mPm.getInstallerPackageName(pkgName), 0, callingUserId)) {
+ if (callingUid == mPm.mIPackageManager.getPackageUid(
+ mPm.mIPackageManager.getInstallerPackageName(pkgName), 0, callingUserId)) {
return true;
}
// Allow package verifier to silently uninstall.
- if (mPm.mRequiredVerifierPackage != null && callingUid == mPm.getPackageUid(
- mPm.mRequiredVerifierPackage, 0, callingUserId)) {
+ if (mPm.mRequiredVerifierPackage != null && callingUid == mPm.mIPackageManager
+ .getPackageUid(mPm.mRequiredVerifierPackage, 0, callingUserId)) {
return true;
}
// Allow package uninstaller to silently uninstall.
- if (mPm.mRequiredUninstallerPackage != null && callingUid == mPm.getPackageUid(
- mPm.mRequiredUninstallerPackage, 0, callingUserId)) {
+ if (mPm.mRequiredUninstallerPackage != null && callingUid == mPm.mIPackageManager
+ .getPackageUid(mPm.mRequiredUninstallerPackage, 0, callingUserId)) {
return true;
}
// Allow storage manager to silently uninstall.
- if (mPm.mStorageManagerPackage != null && callingUid == mPm.getPackageUid(
+ if (mPm.mStorageManagerPackage != null && callingUid == mPm.mIPackageManager.getPackageUid(
mPm.mStorageManagerPackage, 0, callingUserId)) {
return true;
}
// Allow caller having MANAGE_PROFILE_AND_DEVICE_OWNERS permission to silently
// uninstall for device owner provisioning.
- return mPm.checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid)
+ return mPm.mIPackageManager.checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid)
== PERMISSION_GRANTED;
}
- private int[] getBlockUninstallForUsers(String packageName, int[] userIds) {
+ private int[] getBlockUninstallForUsers(@NonNull Computer snapshot, String packageName,
+ int[] userIds) {
int[] result = EMPTY_INT_ARRAY;
for (int userId : userIds) {
- if (mPm.getBlockUninstallForUser(packageName, userId)) {
+ if (snapshot.getBlockUninstallForUser(packageName, userId)) {
result = ArrayUtils.appendInt(result, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index c8326931f6fd..74ea7cc9b4e1 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -304,7 +304,8 @@ final class DexOptHelper {
/*package*/ boolean performDexOpt(DexoptOptions options) {
if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
- } else if (mPm.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
+ } else if (mPm.mIPackageManager.isInstantApp(options.getPackageName(),
+ UserHandle.getCallingUserId())) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 2b46ed404945..05ef3c4ec300 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -288,6 +288,8 @@ final class DumpHelper {
ipw.decreaseIndent();
}
+ final Computer snapshot = mPm.snapshotComputer();
+
if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
&& packageName == null) {
final String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
@@ -299,14 +301,14 @@ final class DumpHelper {
pw.print(" Required: ");
pw.print(requiredVerifierPackage);
pw.print(" (uid=");
- pw.print(mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
+ pw.print(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
pw.println(")");
} else if (requiredVerifierPackage != null) {
pw.print("vrfy,"); pw.print(requiredVerifierPackage);
pw.print(",");
- pw.println(mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
+ pw.println(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
}
}
@@ -324,14 +326,14 @@ final class DumpHelper {
pw.print(" Using: ");
pw.print(verifierPackageName);
pw.print(" (uid=");
- pw.print(mPm.getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
+ pw.print(snapshot.getPackageUid(verifierPackageName,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
pw.println(")");
} else if (verifierPackageName != null) {
pw.print("dv,"); pw.print(verifierPackageName);
pw.print(",");
- pw.println(mPm.getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
+ pw.println(snapshot.getPackageUid(verifierPackageName,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
}
} else {
pw.println();
@@ -405,7 +407,7 @@ final class DumpHelper {
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
- mPm.mComponentResolver.dumpContentProviders(mPm.snapshotComputer(), pw, dumpState,
+ mPm.mComponentResolver.dumpContentProviders(snapshot, pw, dumpState,
packageName);
}
@@ -661,13 +663,14 @@ final class DumpHelper {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mPm.mLock) {
+ final Computer snapshot = mPm.snapshotComputer();
final long requiredVerifierPackageToken =
proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
mPm.mRequiredVerifierPackage);
proto.write(
PackageServiceDumpProto.PackageShortProto.UID,
- mPm.getPackageUid(
+ snapshot.getPackageUid(
mPm.mRequiredVerifierPackage,
MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
@@ -682,7 +685,7 @@ final class DumpHelper {
proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
proto.write(
PackageServiceDumpProto.PackageShortProto.UID,
- mPm.getPackageUid(
+ snapshot.getPackageUid(
verifierPackageName,
MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index c65c2b112706..b4bcd5b3308c 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -22,11 +22,12 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.os.Process;
-import android.os.ServiceManager;
import android.util.EventLog;
import android.util.Log;
+import com.android.server.LocalServices;
import com.android.server.pm.dex.DynamicCodeLogger;
import libcore.util.HexEncoding;
@@ -133,8 +134,7 @@ public class DynamicCodeLoggingService extends JobService {
}
private static DynamicCodeLogger getDynamicCodeLogger() {
- PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
- return pm.getDexManager().getDynamicCodeLogger();
+ return LocalServices.getService(PackageManagerInternal.class).getDynamicCodeLogger();
}
private class IdleLoggingThread extends Thread {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c5c39f82a1fd..8667ffd7930c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -86,6 +86,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -287,6 +288,12 @@ final class InstallPackageHelper {
SharedUserSetting sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(pkgSetting);
if (sharedUserSetting != null) {
sharedUserSetting.addPackage(pkgSetting);
+ if (parsedPackage.isLeavingSharedUid()
+ && SharedUidMigration.applyStrategy(BEST_EFFORT)
+ && sharedUserSetting.isSingleUser()) {
+ // Attempt the transparent shared UID migration
+ mPm.mSettings.convertSharedUserSettingsLPw(sharedUserSetting);
+ }
}
if (reconciledPkg.mInstallArgs != null
&& reconciledPkg.mInstallArgs.mForceQueryableOverride) {
@@ -951,10 +958,6 @@ final class InstallPackageHelper {
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.needsNewAppId()) {
- request.mInstallResult.mRemovedInfo.mNewAppId =
- result.mPkgSetting.getAppId();
- }
} catch (PackageManagerException e) {
request.mInstallResult.setError("Scanning Failed.", e);
return;
@@ -2216,23 +2219,8 @@ final class InstallPackageHelper {
}
incrementalStorages.add(storage);
}
- int previousAppId = 0;
- if (reconciledPkg.mScanResult.needsNewAppId()) {
- // Only set previousAppId if the app is migrating out of shared UID
- previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
-
- if (pkg.shouldInheritKeyStoreKeys()) {
- // Migrate keystore data
- mAppDataHelper.migrateKeyStoreData(
- previousAppId, reconciledPkg.mPkgSetting.getAppId());
- }
-
- if (reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId == previousAppId) {
- // If the previous app ID is removed, clear the keys
- mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, previousAppId);
- }
- }
- mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
+ // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
+ mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
@@ -2602,8 +2590,6 @@ final class InstallPackageHelper {
final int dataLoaderType = installArgs.mDataLoaderType;
final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
- final int previousAppId = (res.mRemovedInfo != null && res.mRemovedInfo.mNewAppId >= 0)
- ? res.mRemovedInfo.mUid : Process.INVALID_UID;
final String packageName = res.mName;
final PackageStateInternal pkgSetting =
succeeded ? mPm.getPackageStateInternal(packageName) : null;
@@ -2711,10 +2697,7 @@ final class InstallPackageHelper {
// Send added for users that don't see the package for the first time
Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (previousAppId != Process.INVALID_UID) {
- extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
- extras.putInt(Intent.EXTRA_PREVIOUS_UID, previousAppId);
- } else if (update) {
+ if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
@@ -2757,27 +2740,24 @@ final class InstallPackageHelper {
// Send replaced for users that don't see the package for the first time
if (update) {
- // Only send PACKAGE_REPLACED if appId has not changed
- if (previousAppId == Process.INVALID_UID) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
- }
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
- }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
@@ -2875,13 +2855,14 @@ final class InstallPackageHelper {
VMRuntime.getRuntime().requestConcurrentGC();
}
+ final Computer snapshot = mPm.snapshotComputer();
// Notify DexManager that the package was installed for new users.
// The updated users should already be indexed and the package code paths
// should not change.
// Don't notify the manager for ephemeral apps as they are not expected to
// survive long enough to benefit of background optimizations.
for (int userId : firstUserIds) {
- PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId);
+ PackageInfo info = snapshot.getPackageInfo(packageName, /*flags*/ 0, userId);
// There's a race currently where some install events may interleave with an
// uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
@@ -3026,8 +3007,7 @@ final class InstallPackageHelper {
installPackageFromSystemLIF(stubPkg.getPath(),
mPm.mUserManager.getUserIds() /*allUserHandles*/,
null /*origUserHandles*/,
- true /*writeSettings*/,
- Process.INVALID_UID /*previousAppId*/);
+ true /*writeSettings*/);
} catch (PackageManagerException pme) {
// Serious WTF; we have to be able to install the stub
Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -3154,10 +3134,8 @@ final class InstallPackageHelper {
try {
synchronized (mPm.mInstallLock) {
final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers;
- final int previousAppId = disabledPs.getAppId() != deletedPs.getAppId()
- ? deletedPs.getAppId() : Process.INVALID_UID;
installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- origUsers, writeSettings, previousAppId);
+ origUsers, writeSettings);
}
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
@@ -3200,7 +3178,7 @@ final class InstallPackageHelper {
@GuardedBy("mPm.mInstallLock")
private void installPackageFromSystemLIF(@NonNull String codePathString,
@NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
- boolean writeSettings, int previousAppId)
+ boolean writeSettings)
throws PackageManagerException {
final File codePath = new File(codePathString);
@ParsingPackageUtils.ParseFlags int parseFlags =
@@ -3223,13 +3201,12 @@ final class InstallPackageHelper {
mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
- setPackageInstalledForSystemPackage(pkg, allUserHandles,
- origUserHandles, writeSettings, previousAppId);
+ setPackageInstalledForSystemPackage(pkg, allUserHandles, origUserHandles, writeSettings);
}
private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
@NonNull int[] allUserHandles, @Nullable int[] origUserHandles,
- boolean writeSettings, int previousAppId) {
+ boolean writeSettings) {
// writer
synchronized (mPm.mLock) {
PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3263,7 +3240,7 @@ final class InstallPackageHelper {
// The method below will take care of removing obsolete permissions and granting
// install permissions.
- mPm.mPermissionManager.onPackageInstalled(pkg, previousAppId,
+ mPm.mPermissionManager.onPackageInstalled(pkg, Process.INVALID_UID,
PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
UserHandle.USER_ALL);
for (final int userId : allUserHandles) {
@@ -3701,7 +3678,14 @@ final class InstallPackageHelper {
}
disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
parsedPackage.getPackageName());
- if (parsedPackage.getSharedUserId() != null && !parsedPackage.isLeavingSharedUid()) {
+
+ boolean ignoreSharedUserId = false;
+ if (installedPkgSetting == null) {
+ // We can directly ignore sharedUserSetting for new installs
+ ignoreSharedUserId = parsedPackage.isLeavingSharedUid();
+ }
+
+ if (!ignoreSharedUserId && parsedPackage.getSharedUserId() != null) {
sharedUserSetting = mPm.mSettings.getSharedUserLPw(
parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 6c80976c0f78..18d2b0c23320 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -289,8 +289,8 @@ final class InstallParams extends HandlerParams {
*/
private int fixUpInstallReason(String installerPackageName, int installerUid,
int installReason) {
- if (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
- == PERMISSION_GRANTED) {
+ if (mPm.snapshotComputer().checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
+ installerUid) == PERMISSION_GRANTED) {
// If the install is being performed by a system app, we trust that app to have set the
// install reason correctly.
return installReason;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 36bad3e604d5..76b9830cbde9 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -220,6 +220,8 @@ public class Installer extends SystemService {
if (!checkBeforeRemote()) {
return buildPlaceholderCreateAppDataResult();
}
+ // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
+ args.previousAppId = 0;
try {
return mInstalld.createAppData(args);
} catch (Exception e) {
@@ -234,6 +236,10 @@ public class Installer extends SystemService {
Arrays.fill(results, buildPlaceholderCreateAppDataResult());
return results;
}
+ // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
+ for (final CreateAppDataArgs arg : args) {
+ arg.previousAppId = 0;
+ }
try {
return mInstalld.createAppDataBatched(args);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/pm/IntentResolverInterceptor.java b/services/core/java/com/android/server/pm/IntentResolverInterceptor.java
new file mode 100644
index 000000000000..0ee07b650cf5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentResolverInterceptor.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.wm.ActivityInterceptorCallback.INTENT_RESOLVER_ORDERED_ID;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * Service to register an {@code ActivityInterceptorCallback} that modifies any {@code Intent}
+ * that's being used to launch a user-space {@code ChooserActivity}, by adding
+ * EXTRA_PERMISSION_TOKEN, a Binder representing a single-use-only permission to invoke the
+ * #startActivityAsCaller() API (which normally isn't available in user-space); and setting the
+ * destination component to the delegated component when appropriate.
+ */
+public final class IntentResolverInterceptor {
+ private static final String TAG = "IntentResolverIntercept";
+
+ private final Context mContext;
+ private boolean mUseDelegateChooser;
+
+ private final ActivityInterceptorCallback mActivityInterceptorCallback =
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (mUseDelegateChooser && isChooserActivity(info)) {
+ return new ActivityInterceptResult(
+ modifyChooserIntent(info.intent),
+ info.checkedOptions);
+ }
+ return null;
+ }
+ };
+
+ public IntentResolverInterceptor(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Start listening for intents and USE_DELEGATE_CHOOSER property changes.
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public void registerListeners() {
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .registerActivityStartInterceptor(INTENT_RESOLVER_ORDERED_ID,
+ mActivityInterceptorCallback);
+
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mContext.getMainExecutor(), properties -> updateUseDelegateChooser());
+ updateUseDelegateChooser();
+ }
+
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ private void updateUseDelegateChooser() {
+ mUseDelegateChooser = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
+ false);
+ }
+
+ private Intent modifyChooserIntent(Intent intent) {
+ intent.setComponent(getUnbundledChooserComponentName());
+ addStartActivityPermissionTokenToIntent(intent, getUnbundledChooserComponentName());
+ return intent;
+ }
+
+ private static boolean isChooserActivity(ActivityInterceptorInfo info) {
+ ComponentName targetComponent = new ComponentName(info.aInfo.packageName, info.aInfo.name);
+
+ return targetComponent.equals(getSystemChooserComponentName())
+ || targetComponent.equals(getUnbundledChooserComponentName());
+ }
+
+ private static Intent addStartActivityPermissionTokenToIntent(
+ Intent intent, ComponentName grantee) {
+ try {
+ intent.putExtra(
+ ActivityTaskManager.EXTRA_PERMISSION_TOKEN,
+ ActivityTaskManager.getService().requestStartActivityPermissionToken(grantee));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to add permission token to chooser intent");
+ }
+ return intent;
+ }
+
+ private static ComponentName getSystemChooserComponentName() {
+ return new ComponentName("android", "com.android.internal.app.ChooserActivity");
+ }
+
+ private static ComponentName getUnbundledChooserComponentName() {
+ return ComponentName.unflattenFromString(
+ Resources.getSystem().getString(R.string.config_chooserActivity));
+ }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6b3ce773fb63..5e0fc3bf91e7 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -320,8 +320,12 @@ public class LauncherAppsService extends SystemService {
private PackageInstallerService getPackageInstallerService() {
if (mPackageInstallerService == null) {
- mPackageInstallerService = ((PackageInstallerService) ((PackageManagerService)
- ServiceManager.getService("package")).getPackageInstaller());
+ try {
+ mPackageInstallerService = ((PackageInstallerService) ((IPackageManager)
+ ServiceManager.getService("package")).getPackageInstaller());
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Error gettig IPackageInstaller", e);
+ }
}
return mPackageInstallerService;
}
@@ -1109,13 +1113,11 @@ public class LauncherAppsService extends SystemService {
// Note the target activity doesn't have to be exported.
// Flag for bubble
- if (startActivityOptions != null) {
- ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
- if (options.isApplyActivityFlagsForBubbles()) {
- // Flag for bubble to make behaviour match documentLaunchMode=always.
- intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- }
+ ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
+ if (options != null && options.isApplyActivityFlagsForBubbles()) {
+ // Flag for bubble to make behaviour match documentLaunchMode=always.
+ intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fe2fe097bdf6..6613f016f66a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -334,7 +334,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
StagingManager.StagedSession stagedSession = session.mStagedSession;
if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
&& getSession(stagedSession.getParentSessionId()) == null) {
- stagedSession.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ stagedSession.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
"An orphan staged session " + stagedSession.sessionId() + " is found, "
+ "parent " + stagedSession.getParentSessionId() + " is missing");
continue;
@@ -676,7 +676,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
String originatingPackageName = null;
if (params.originatingUid != SessionParams.UID_UNKNOWN
&& params.originatingUid != callingUid) {
- String[] packages = mPm.getPackagesForUid(params.originatingUid);
+ String[] packages = mPm.mIPackageManager.getPackagesForUid(params.originatingUid);
if (packages != null && packages.length > 0) {
// Choose an arbitrary representative package in the case of a shared UID.
originatingPackageName = packages[0];
@@ -727,7 +727,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
&& !isCalledBySystemOrShell(callingUid)
- && (mPm.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ && (mPm.mIPackageManager.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM)
+ == 0) {
throw new SecurityException(
"Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag.");
}
@@ -852,7 +853,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
- false, false, false, SessionInfo.SESSION_NO_ERROR, "");
+ false, false, false, PackageManager.INSTALL_UNKNOWN, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
@@ -1153,7 +1154,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
.setAdmin(callerPackageName)
.write();
} else {
- ApplicationInfo appInfo = mPm.getApplicationInfo(callerPackageName, 0, userId);
+ ApplicationInfo appInfo = mPm.mIPackageManager
+ .getApplicationInfo(callerPackageName, 0, userId);
if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
null);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 55fc78599661..35a7eaf29ddc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -82,7 +82,6 @@ import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -462,7 +461,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private boolean mSessionFailed;
@GuardedBy("mLock")
- private int mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+ private int mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
@GuardedBy("mLock")
private String mSessionErrorMessage;
@@ -817,25 +816,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
// It is safe to access mInstallerUid and mInstallSource without lock
// because they are immutable after sealing.
+ final Computer snapshot = mPm.snapshotComputer();
final boolean isInstallPermissionGranted =
- (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
+ (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
final boolean isSelfUpdatePermissionGranted =
- (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
+ (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
final boolean isUpdatePermissionGranted =
- (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
+ (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
+ final boolean isUpdateWithoutUserActionPermissionGranted = (snapshot.checkUidPermission(
android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
== PackageManager.PERMISSION_GRANTED);
- final boolean isInstallDpcPackagesPermissionGranted = (mPm.checkUidPermission(
+ final boolean isInstallDpcPackagesPermissionGranted = (snapshot.checkUidPermission(
android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid)
== PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
+ final int targetPackageUid = snapshot.getPackageUid(packageName, 0, userId);
final boolean isUpdate = targetPackageUid != -1 || isApexSession();
final InstallSourceInfo existingInstallSourceInfo = isUpdate
- ? mPm.getInstallSourceInfo(packageName)
+ ? snapshot.getInstallSourceInfo(packageName)
: null;
final String existingInstallerPackageName = existingInstallSourceInfo != null
? existingInstallSourceInfo.getInstallingPackageName()
@@ -2009,12 +2009,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void transfer(String packageName) {
Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
- ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
+ final Computer snapshot = mPm.snapshotComputer();
+ ApplicationInfo newOwnerAppInfo = snapshot.getApplicationInfo(packageName, 0, userId);
if (newOwnerAppInfo == null) {
throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
}
- if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
+ if (PackageManager.PERMISSION_GRANTED != snapshot.checkUidPermission(
Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
throw new SecurityException("Destination package " + packageName + " does not have "
+ "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
@@ -2329,7 +2330,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
} else {
PackageManagerException e = (PackageManagerException) t.getCause();
- setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ setSessionFailed(e.error,
PackageManager.installStatusToString(e.error, e.getMessage()));
dispatchSessionFinished(e.error, e.getMessage(), null);
maybeFinishChildSessions(e.error, e.getMessage());
@@ -2509,7 +2510,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Package didn't install; no valid uid
packageUid = Process.INVALID_UID;
} else {
- packageUid = mPm.getPackageUid(packageName, 0, userId);
+ packageUid = mPm.snapshotComputer().getPackageUid(packageName, 0, userId);
}
FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
isIncrementalInstallation(),
@@ -2667,7 +2668,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mResolvedStagedFiles.clear();
mResolvedInheritedFiles.clear();
- final PackageInfo pkgInfo = mPm.getPackageInfo(
+ final PackageInfo pkgInfo = mPm.snapshotComputer().getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES /*flags*/, userId);
@@ -2860,6 +2861,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
inheritFileLocked(mResolvedBaseFile);
// Collect the requiredSplitTypes from base
CollectionUtils.addAll(requiredSplitTypes, existing.getBaseRequiredSplitTypes());
+ } else {
+ // Installing base.apk. Make sure the app is restarted.
+ params.setDontKillApp(false);
}
// Inherit splits if not overridden.
@@ -3556,6 +3560,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
+ public int getInstallFlags() {
+ return params.installFlags;
+ }
+
+ @Override
public DataLoaderParamsParcel getDataLoaderParams() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
@@ -3778,8 +3787,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
};
try {
- final PackageInfo pkgInfo = mPm.getPackageInfo(this.params.appPackageName, 0,
- userId);
+ final PackageInfo pkgInfo = mPm.snapshotComputer()
+ .getPackageInfo(this.params.appPackageName, 0, userId);
final File inheritedDir =
(pkgInfo != null && pkgInfo.applicationInfo != null) ? new File(
pkgInfo.applicationInfo.getCodePath()).getParentFile() : null;
@@ -4035,7 +4044,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSessionReady = true;
mSessionApplied = false;
mSessionFailed = false;
- mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+ mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
mSessionErrorMessage = "";
}
mCallback.onSessionChanged(this);
@@ -4063,7 +4072,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSessionReady = false;
mSessionApplied = true;
mSessionFailed = false;
- mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+ mSessionErrorCode = INSTALL_SUCCEEDED;
mSessionErrorMessage = "";
Slog.d(TAG, "Marking session " + sessionId + " as applied");
}
@@ -4093,7 +4102,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/** {@hide} */
- @SessionErrorCode
int getSessionErrorCode() {
synchronized (mLock) {
return mSessionErrorCode;
@@ -4524,8 +4532,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
final String installerAttributionTag = readStringAttribute(in,
ATTR_INSTALLER_ATTRIBUTION_TAG);
- final int installerUid = in.getAttributeInt(null, ATTR_INSTALLER_UID, pm.getPackageUid(
- installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
+ final int installerUid = in.getAttributeInt(null, ATTR_INSTALLER_UID, pm.snapshotComputer()
+ .getPackageUid(installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ userId));
final String installInitiatingPackageName =
readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME);
final String installOriginatingPackageName =
@@ -4581,7 +4590,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
- SessionInfo.SESSION_NO_ERROR);
+ PackageManager.INSTALL_UNKNOWN);
final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
index 37daf112aa69..9a43008acfdf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerNative.java
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -71,7 +71,7 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public String[] getAllPackages() {
- return mPm.getAllPackages().toArray(new String[0]);
+ return mPm.snapshotComputer().getAllPackages().toArray(new String[0]);
}
@Override
@@ -82,7 +82,7 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
if (uids == null || uids.length == 0) {
return null;
}
- names = mPm.getNamesForUids(uids);
+ names = mPm.snapshotComputer().getNamesForUids(uids);
results = (names != null) ? names : new String[uids.length];
// massage results so they can be parsed by the native binder
for (int i = results.length - 1; i >= 0; --i) {
@@ -104,13 +104,14 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
// NB: this differentiates between preloads and sideloads
@Override
public String getInstallerForPackage(String packageName) throws RemoteException {
- final String installerName = mPm.getInstallerPackageName(packageName);
+ final Computer snapshot = mPm.snapshotComputer();
+ final String installerName = snapshot.getInstallerPackageName(packageName);
if (!TextUtils.isEmpty(installerName)) {
return installerName;
}
// differentiate between preload and sideload
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
- ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+ ApplicationInfo appInfo = snapshot.getApplicationInfo(packageName,
/*flags*/ 0,
/*userId*/ callingUser);
if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -123,7 +124,8 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
public long getVersionCodeForPackage(String packageName) throws RemoteException {
try {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
- PackageInfo pInfo = mPm.getPackageInfo(packageName, 0, callingUser);
+ PackageInfo pInfo = mPm.snapshotComputer()
+ .getPackageInfo(packageName, 0, callingUser);
if (pInfo != null) {
return pInfo.getLongVersionCode();
}
@@ -134,7 +136,7 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
- int targetSdk = mPm.getTargetSdkVersion(packageName);
+ int targetSdk = mPm.snapshotComputer().getTargetSdkVersion(packageName);
if (targetSdk != -1) {
return targetSdk;
}
@@ -145,7 +147,8 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public boolean isPackageDebuggable(String packageName) throws RemoteException {
int callingUser = UserHandle.getCallingUserId();
- ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0, callingUser);
+ ApplicationInfo appInfo = mPm.snapshotComputer()
+ .getApplicationInfo(packageName, 0, callingUser);
if (appInfo != null) {
return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
}
@@ -157,9 +160,10 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ final Computer snapshot = mPm.snapshotComputer();
boolean[] results = new boolean[packageNames.length];
for (int i = results.length - 1; i >= 0; --i) {
- ApplicationInfo appInfo = mPm.getApplicationInfo(packageNames[i], 0, callingUser);
+ ApplicationInfo appInfo = snapshot.getApplicationInfo(packageNames[i], 0, callingUser);
results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
}
return results;
@@ -168,7 +172,7 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public int getLocationFlags(String packageName) throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
- ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+ ApplicationInfo appInfo = mPm.snapshotComputer().getApplicationInfo(packageName,
/*flags*/ 0,
/*userId*/ callingUser);
if (appInfo == null) {
@@ -188,7 +192,8 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
throws RemoteException {
- return mPm.hasSigningCertificate(packageName, certificate, CERT_INPUT_SHA256);
+ return mPm.snapshotComputer()
+ .hasSigningCertificate(packageName, certificate, CERT_INPUT_SHA256);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f07418f6007f..e20a861e2eae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,11 +105,6 @@ import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
-import android.content.pm.PackageManager.ComponentType;
-import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
-import android.content.pm.PackageManager.ModuleInfoFlags;
-import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackagePartitions;
import android.content.pm.ParceledListSlice;
@@ -218,6 +213,7 @@ import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtUtils;
import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -334,8 +330,8 @@ import java.util.function.Consumer;
* $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
* </pre>
*/
-public class PackageManagerService extends IPackageManager.Stub
- implements PackageSender, TestUtilityService {
+public class PackageManagerService implements PackageSender, TestUtilityService {
+
static final String TAG = "PackageManager";
public static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
@@ -648,6 +644,8 @@ public class PackageManagerService extends IPackageManager.Stub
*/
boolean mPromoteSystemApps;
+ // TODO: Make IPackageManager reference private to hide discouraged APIs
+ final IPackageManagerImpl mIPackageManager;
private final PackageManagerInternal mPmInternal;
private final TestUtilityService mTestUtilityService;
@@ -958,6 +956,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
private final SuspendPackageHelper mSuspendPackageHelper;
+ private final IntentResolverInterceptor mIntentResolverInterceptor;
/**
* Invalidate the package info cache, which includes updating the cached computer.
@@ -1059,7 +1058,7 @@ public class PackageManagerService extends IPackageManager.Stub
private volatile Computer mSnapshotComputer;
// A trampoline that directs callers to either the live or snapshot computer.
- private final ComputerTracker mComputer = new ComputerTracker(this);
+ final ComputerTracker mComputer = new ComputerTracker(this);
// If true, the snapshot is invalid (stale). The attribute is static since it may be
// set from outside classes. The attribute may be set to true anywhere, although it
@@ -1167,15 +1166,6 @@ public class PackageManagerService extends IPackageManager.Stub
onChange(null);
}
- @Override
- public void notifyPackagesReplacedReceived(String[] packages) {
- Computer computer = snapshotComputer();
- ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
- for (int index = 0; index < packagesToNotify.size(); index++) {
- notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */);
- }
- }
-
void notifyInstallObserver(String packageName, boolean killApp) {
final Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
killApp ? mPendingKillInstallObservers.remove(packageName)
@@ -1232,16 +1222,6 @@ public class PackageManagerService extends IPackageManager.Stub
PRUNE_UNUSED_SHARED_LIBRARIES_DELAY);
}
- @Override
- public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits,
- @Checksum.TypeMask int optional, @Checksum.TypeMask int required,
- @Nullable List trustedInstallers,
- @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
- requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
- onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
- mInjector.getBackgroundHandler());
- }
-
/**
* Requests checksums for the APK file.
* See {@link PackageInstaller.Session#requestChecksums} for details.
@@ -1289,7 +1269,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (applicationInfo == null) {
throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
}
- final InstallSourceInfo installSourceInfo = getInstallSourceInfo(packageName);
+ final InstallSourceInfo installSourceInfo =
+ mIPackageManager.getInstallSourceInfo(packageName);
final String installerPackageName =
installSourceInfo != null ? installSourceInfo.getInitiatingPackageName() : null;
@@ -1431,9 +1412,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- public static PackageManagerService main(Context context, Installer installer,
- @NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
- boolean onlyCore) {
+ public static Pair<PackageManagerService, IPackageManager> main(Context context,
+ Installer installer, @NonNull DomainVerificationService domainVerificationService,
+ boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -1464,10 +1445,10 @@ public class PackageManagerService extends IPackageManager.Stub
(i, pm) -> SystemConfig.getInstance(),
(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
i.getContext(), "*dexopt*"),
- (i, pm) -> new DexManager(i.getContext(), pm, i.getPackageDexOptimizer(),
+ (i, pm) -> new DexManager(i.getContext(), pm.mIPackageManager,
+ i.getPackageDexOptimizer(), i.getInstaller(), i.getInstallLock()),
+ (i, pm) -> new ArtManagerService(i.getContext(), pm.mIPackageManager,
i.getInstaller(), i.getInstallLock()),
- (i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(),
- i.getInstallLock()),
(i, pm) -> ApexManager.getInstance(),
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
@@ -1489,7 +1470,7 @@ public class PackageManagerService extends IPackageManager.Stub
i.getContext(), pm, i::getScanningPackageParser),
(i, pm, cn) -> new InstantAppResolverConnection(
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
- (i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
+ (i, pm) -> new ModuleInfoProvider(i.getContext(), pm.mIPackageManager),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
(i, pm) -> domainVerificationService,
(i, pm) -> {
@@ -1550,11 +1531,11 @@ public class PackageManagerService extends IPackageManager.Stub
selinuxChangeListener);
m.installAllowlistedSystemPackages();
- ServiceManager.addService("package", m);
+ ServiceManager.addService("package", m.mIPackageManager);
final PackageManagerNative pmn = new PackageManagerNative(m);
ServiceManager.addService("package_native", pmn);
LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
- return m;
+ return Pair.create(m, m.mIPackageManager);
}
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
@@ -1660,6 +1641,7 @@ public class PackageManagerService extends IPackageManager.Stub
mPackageDexOptimizer = testParams.packageDexOptimizer;
mPackageParserCallback = testParams.packageParserCallback;
mPendingBroadcasts = testParams.pendingPackageBroadcasts;
+ mIPackageManager = new IPackageManagerImpl();
mPmInternal = testParams.pmInternal;
mTestUtilityService = testParams.testUtilityService;
mProcessLoggingHandler = testParams.processLoggingHandler;
@@ -1712,6 +1694,8 @@ public class PackageManagerService extends IPackageManager.Stub
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
+ mIntentResolverInterceptor = null;
+
registerObservers(false);
invalidatePackageInfoCache();
}
@@ -1719,6 +1703,7 @@ public class PackageManagerService extends IPackageManager.Stub
public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
+ mIPackageManager = new IPackageManagerImpl();
mIsEngBuild = isEngBuild;
mIsUserDebugBuild = isUserDebugBuild;
mSdkVersion = sdkVersion;
@@ -1770,7 +1755,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean hasFeature(String feature) {
- return PackageManagerService.this.hasSystemFeature(feature, 0);
+ return PackageManagerService.this.mIPackageManager.hasSystemFeature(feature, 0);
}
};
@@ -1997,11 +1982,13 @@ public class PackageManagerService extends IPackageManager.Stub
mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
mComponentResolver.fixProtectedFilterPriorities(mPmInternal.getSetupWizardPackageName());
- mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
- mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
+ mDefaultTextClassifierPackage = mIPackageManager.getDefaultTextClassifierPackageName();
+ mSystemTextClassifierPackageName =
+ mIPackageManager.getSystemTextClassifierPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
- mAppPredictionServicePackage = getAppPredictionServicePackageName();
- mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
+ mAppPredictionServicePackage = mIPackageManager.getAppPredictionServicePackageName();
+ mIncidentReportApproverPackage =
+ mIPackageManager.getIncidentReportApproverPackageName();
mRetailDemoPackage = getRetailDemoPackageName();
mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
mRecentsPackage = getRecentsPackageName();
@@ -2156,7 +2143,7 @@ public class PackageManagerService extends IPackageManager.Stub
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
mSettings.setPermissionControllerVersion(
- getPackageInfo(mRequiredPermissionControllerPackage, 0,
+ mIPackageManager.getPackageInfo(mRequiredPermissionControllerPackage, 0,
UserHandle.USER_SYSTEM).getLongVersionCode());
// Resolve the sdk sandbox package
@@ -2204,7 +2191,8 @@ public class PackageManagerService extends IPackageManager.Stub
// scanning).
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
for (int userId : userIds) {
- userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
+ userPackages.put(userId, mIPackageManager.getInstalledPackages(/*flags*/ 0, userId)
+ .getList());
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
@@ -2240,6 +2228,8 @@ public class PackageManagerService extends IPackageManager.Stub
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
+ mIntentResolverInterceptor = new IntentResolverInterceptor(mContext);
+
Slog.i(TAG, "Fix for b/169414761 is applied");
}
@@ -2255,19 +2245,16 @@ public class PackageManagerService extends IPackageManager.Stub
setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
}
- @Override
public boolean isFirstBoot() {
// allow instant applications
return mFirstBoot;
}
- @Override
public boolean isOnlyCoreApps() {
// allow instant applications
return mOnlyCore;
}
- @Override
public boolean isDeviceUpgrading() {
// allow instant applications
// The system property allows testing ota flow when upgraded to the same image.
@@ -2388,8 +2375,9 @@ public class PackageManagerService extends IPackageManager.Stub
for (int i = 0; i < N; i++) {
final ResolveInfo cur = matches.get(i);
final String packageName = cur.getComponentInfo().packageName;
- if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
- packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
+ if (mIPackageManager.checkPermission(
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName,
+ UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
continue;
}
@@ -2417,8 +2405,9 @@ public class PackageManagerService extends IPackageManager.Stub
for (int i = 0; i < N; i++) {
final ResolveInfo cur = matches.get(i);
final String packageName = cur.getComponentInfo().packageName;
- if (checkPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
- packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
+ if (mIPackageManager.checkPermission(
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, packageName,
+ UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Domain verification agent found but does not hold permission: "
+ packageName);
continue;
@@ -2441,14 +2430,6 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
- @Override
- public @Nullable ComponentName getInstantAppResolverComponent() {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- return getInstantAppResolver();
- }
-
private @Nullable ComponentName getInstantAppResolver() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
@@ -2538,7 +2519,7 @@ public class PackageManagerService extends IPackageManager.Stub
Iterator<ResolveInfo> iter = matches.iterator();
while (iter.hasNext()) {
final ResolveInfo rInfo = iter.next();
- if (checkPermission(
+ if (mIPackageManager.checkPermission(
Manifest.permission.INSTALL_PACKAGES,
rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) {
continue;
@@ -2569,18 +2550,18 @@ public class PackageManagerService extends IPackageManager.Stub
return matches.get(0).getComponentInfo().getComponentName();
}
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (RuntimeException e) {
- if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)
- && !(e instanceof ParcelableException)) {
- Slog.wtf(TAG, "Package Manager Unexpected Exception", e);
- }
- throw e;
- }
+ /**
+ * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int)
+ */
+ boolean shouldFilterApplication(
+ @Nullable PackageStateInternal ps, int callingUid, int userId) {
+ return mComputer.shouldFilterApplication(
+ ps, callingUid, userId);
+ }
+
+ private @PackageStartability int getPackageStartability(String packageName,
+ int callingUid, int userId) {
+ return mComputer.getPackageStartability(mSafeMode, packageName, callingUid, userId);
}
/**
@@ -2606,118 +2587,11 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.generatePackageInfo(ps, flags, userId);
}
- @Override
- public void checkPackageStartable(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- throw new SecurityException("Instant applications don't have access to this method");
- }
- if (!mUserManager.exists(userId)) {
- throw new SecurityException("User doesn't exist");
- }
- enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable");
- switch (getPackageStartability(packageName, callingUid, userId)) {
- case PACKAGE_STARTABILITY_NOT_FOUND:
- throw new SecurityException("Package " + packageName + " was not found!");
- case PACKAGE_STARTABILITY_NOT_SYSTEM:
- throw new SecurityException("Package " + packageName + " not a system app!");
- case PACKAGE_STARTABILITY_FROZEN:
- throw new SecurityException("Package " + packageName + " is currently frozen!");
- case PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED:
- throw new SecurityException("Package " + packageName + " is not encryption aware!");
- case PACKAGE_STARTABILITY_OK:
- default:
- }
- }
-
- private @PackageStartability int getPackageStartability(String packageName,
- int callingUid, int userId) {
- return mComputer.getPackageStartability(mSafeMode, packageName, callingUid, userId);
- }
-
- @Override
- public boolean isPackageAvailable(String packageName, int userId) {
- return mComputer.isPackageAvailable(packageName, userId);
- }
-
- @Override
- public PackageInfo getPackageInfo(String packageName,
- @PackageManager.PackageInfoFlagsBits long flags, int userId) {
- return mComputer.getPackageInfo(packageName, flags, userId);
- }
-
- @Override
- public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
- @PackageManager.PackageInfoFlagsBits long flags, int userId) {
- return mComputer.getPackageInfoInternal(versionedPackage.getPackageName(),
- versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
- }
-
- /**
- * Returns whether or not access to the application should be filtered.
- * <p>
- * Access may be limited based upon whether the calling or target applications
- * are instant applications.
- *
- * @see #canViewInstantApps(int, int)
- */
- private boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid,
- @Nullable ComponentName component, @ComponentType int componentType, int userId) {
- return mComputer.shouldFilterApplication(ps, callingUid,
- component, componentType, userId);
- }
-
- /**
- * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int)
- */
- boolean shouldFilterApplication(
- @Nullable PackageStateInternal ps, int callingUid, int userId) {
- return mComputer.shouldFilterApplication(
- ps, callingUid, userId);
- }
-
- /**
- * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int)
- */
- private boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid,
- int userId) {
- return mComputer.shouldFilterApplication(sus, callingUid, userId);
- }
-
- private boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
- return mComputer.filterSharedLibPackage(ps, uid, userId, flags);
- }
-
- @Override
- public String[] currentToCanonicalPackageNames(String[] names) {
- return mComputer.currentToCanonicalPackageNames(names);
- }
-
- @Override
- public String[] canonicalToCurrentPackageNames(String[] names) {
- return mComputer.canonicalToCurrentPackageNames(names);
- }
-
- @Override
- public int getPackageUid(@NonNull String packageName,
- @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
- return mComputer.getPackageUid(packageName, flags, userId);
- }
-
int getPackageUidInternal(String packageName,
@PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
}
- @Override
- public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlagsBits long flags,
- int userId) {
- return mComputer.getPackageGids(packageName, flags, userId);
- }
-
- // NOTE: Can't remove due to unsupported app usage
- @Override
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
@@ -2725,18 +2599,6 @@ public class PackageManagerService extends IPackageManager.Stub
.getPermissionGroupInfo(groupName, flags);
}
- private ApplicationInfo generateApplicationInfoFromSettings(String packageName,
- @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
- return mComputer.generateApplicationInfoFromSettings(packageName, flags, filterCallingUid,
- userId);
- }
-
- @Override
- public ApplicationInfo getApplicationInfo(String packageName,
- @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
- return mComputer.getApplicationInfo(packageName, flags, userId);
- }
-
/**
* Important: The provided filterCallingUid is used exclusively to filter out applications
* that can be seen based on user state. It's typically the original caller uid prior
@@ -2750,61 +2612,6 @@ public class PackageManagerService extends IPackageManager.Stub
filterCallingUid, userId);
}
- @Override
- public void deletePreloadsFileCache() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
- "deletePreloadsFileCache");
- File dir = Environment.getDataPreloadsFileCacheDirectory();
- Slog.i(TAG, "Deleting preloaded file cache " + dir);
- FileUtils.deleteContents(dir);
- }
-
- @Override
- public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
- final @StorageManager.AllocateFlags int flags, final IPackageDataObserver observer) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CLEAR_APP_CACHE, null);
- mHandler.post(() -> {
- boolean success = false;
- try {
- freeStorage(volumeUuid, freeStorageSize, flags);
- success = true;
- } catch (IOException e) {
- Slog.w(TAG, e);
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(null, success);
- } catch (RemoteException e) {
- Slog.w(TAG, e);
- }
- }
- });
- }
-
- @Override
- public void freeStorage(final String volumeUuid, final long freeStorageSize,
- final @StorageManager.AllocateFlags int flags, final IntentSender pi) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CLEAR_APP_CACHE, TAG);
- mHandler.post(() -> {
- boolean success = false;
- try {
- freeStorage(volumeUuid, freeStorageSize, flags);
- success = true;
- } catch (IOException e) {
- Slog.w(TAG, e);
- }
- if (pi != null) {
- try {
- pi.sendIntent(null, success ? 1 : 0, null, null, null);
- } catch (SendIntentException e) {
- Slog.w(TAG, e);
- }
- }
- });
- }
-
/**
* Blocking call to clear all cached app data above quota.
*/
@@ -2841,7 +2648,7 @@ public class PackageManagerService extends IPackageManager.Stub
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
if (internalVolume && (aggressive || SystemProperties
.getBoolean("persist.sys.preloads.file_cache_expired", false))) {
- deletePreloadsFileCache();
+ mIPackageManager.deletePreloadsFileCache();
if (file.getUsableSpace() >= bytes) return;
}
@@ -2980,17 +2787,6 @@ public class PackageManagerService extends IPackageManager.Stub
wantInstantApps, isImplicitImageCaptureIntentAndNotSetByDpc);
}
- @Override
- public int getTargetSdkVersion(@NonNull String packageName) {
- return mComputer.getTargetSdkVersion(packageName);
- }
-
- @Override
- public ActivityInfo getActivityInfo(ComponentName component,
- @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
- return mComputer.getActivityInfo(component, flags, userId);
- }
-
/**
* Important: The provided filterCallingUid is used exclusively to filter out activities
* that can be seen based on user state. It's typically the original caller uid prior
@@ -3003,33 +2799,6 @@ public class PackageManagerService extends IPackageManager.Stub
filterCallingUid, userId);
}
- @Override
- public boolean activitySupportsIntent(ComponentName component, Intent intent,
- String resolvedType) {
- return mComputer.activitySupportsIntent(mResolveComponentName, component, intent,
- resolvedType);
- }
-
- @Override
- public ActivityInfo getReceiverInfo(ComponentName component,
- @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
- return mComputer.getReceiverInfo(component, flags, userId);
- }
-
- @Override
- public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
- @PackageManager.PackageInfoFlagsBits long flags, int userId) {
- return mComputer.getSharedLibraries(packageName, flags, userId);
- }
-
- @Nullable
- @Override
- public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
- @NonNull int userId) {
- return mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
- }
-
@Nullable
List<VersionedPackage> getPackagesUsingSharedLibrary(
SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
@@ -3037,95 +2806,15 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
}
- @Nullable
- @Override
- public ServiceInfo getServiceInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
- return mComputer.getServiceInfo(component, flags, userId);
- }
-
- @Nullable
- @Override
- public ProviderInfo getProviderInfo(@NonNull ComponentName component,
- @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
- return mComputer.getProviderInfo(component, flags, userId);
- }
-
- @Override
- public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) {
+ public ModuleInfo getModuleInfo(String packageName, @PackageManager.ModuleInfoFlags int flags) {
return mModuleInfoProvider.getModuleInfo(packageName, flags);
}
- @Override
- public List<ModuleInfo> getInstalledModules(int flags) {
- return mModuleInfoProvider.getInstalledModules(flags);
- }
-
- @Nullable
- @Override
- public String[] getSystemSharedLibraryNames() {
- return mComputer.getSystemSharedLibraryNames();
- }
-
- @Override
- public @NonNull String getServicesSystemSharedLibraryPackageName() {
- return mServicesExtensionPackageName;
- }
-
- @Override
- public @NonNull String getSharedSystemSharedLibraryPackageName() {
- return mSharedSystemSharedLibraryPackageName;
- }
-
@GuardedBy("mLock")
void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList);
}
- @Override
- public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return null;
- }
- if (!mUserManager.exists(userId)) {
- return null;
- }
- enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
- final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages(
- sequenceNumber, userId);
-
- if (changedPackages != null) {
- final List<String> packageNames = changedPackages.getPackageNames();
- for (int index = packageNames.size() - 1; index >= 0; index--) {
- // Filter out the changes if the calling package should not be able to see it.
- final PackageSetting ps = mSettings.getPackageLPr(packageNames.get(index));
- if (shouldFilterApplication(ps, callingUid, userId)) {
- packageNames.remove(index);
- }
- }
- }
-
- return changedPackages;
- }
-
- @Override
- public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
- // allow instant applications
- ArrayList<FeatureInfo> res;
- synchronized (mAvailableFeatures) {
- res = new ArrayList<>(mAvailableFeatures.size() + 1);
- res.addAll(mAvailableFeatures.values());
- }
- final FeatureInfo fi = new FeatureInfo();
- fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- res.add(fi);
-
- return new ParceledListSlice<>(res);
- }
-
- @Override
public boolean hasSystemFeature(String name, int version) {
// allow instant applications
synchronized (mAvailableFeatures) {
@@ -3139,29 +2828,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
// NOTE: Can't remove due to unsupported app usage
- @Override
public int checkPermission(String permName, String pkgName, int userId) {
return mPermissionManager.checkPermission(pkgName, permName, userId);
}
- // NOTE: Can't remove without a major refactor. Keep around for now.
- @Override
- public int checkUidPermission(String permName, int uid) {
- return mComputer.checkUidPermission(permName, uid);
- }
-
- @Override
- public String getPermissionControllerPackageName() {
- final int callingUid = Binder.getCallingUid();
- if (mComputer.getPackageStateFiltered(mRequiredPermissionControllerPackage,
- callingUid, UserHandle.getUserId(callingUid)) != null) {
- return mRequiredPermissionControllerPackage;
- }
-
- throw new IllegalStateException("PermissionController is not found");
- }
-
- @Override
public String getSdkSandboxPackageName() {
return mRequiredSdkSandboxPackage;
}
@@ -3170,171 +2840,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mRequiredInstallerPackage;
}
- // NOTE: Can't remove due to unsupported app usage
- @Override
- public boolean addPermission(PermissionInfo info) {
- // Because this is accessed via the package manager service AIDL,
- // go through the permission manager service AIDL
- return mContext.getSystemService(PermissionManager.class).addPermission(info, false);
- }
-
- // NOTE: Can't remove due to unsupported app usage
- @Override
- public boolean addPermissionAsync(PermissionInfo info) {
- // Because this is accessed via the package manager service AIDL,
- // go through the permission manager service AIDL
- return mContext.getSystemService(PermissionManager.class).addPermission(info, true);
- }
-
- // NOTE: Can't remove due to unsupported app usage
- @Override
- public void removePermission(String permName) {
- // Because this is accessed via the package manager service AIDL,
- // go through the permission manager service AIDL
- mContext.getSystemService(PermissionManager.class).removePermission(permName);
- }
-
- // NOTE: Can't remove due to unsupported app usage
- @Override
- public void grantRuntimePermission(String packageName, String permName, final int userId) {
- // Because this is accessed via the package manager service AIDL,
- // go through the permission manager service AIDL
- mContext.getSystemService(PermissionManager.class)
- .grantRuntimePermission(packageName, permName, UserHandle.of(userId));
- }
-
- @Override
- public boolean isProtectedBroadcast(String actionName) {
- if (actionName != null) {
- // TODO: remove these terrible hacks
- if (actionName.startsWith("android.net.netmon.lingerExpired")
- || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
- || actionName.startsWith("com.android.internal.telephony.data-reconnect")
- || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
- return true;
- }
- }
- // allow instant applications
- synchronized (mProtectedBroadcasts) {
- return mProtectedBroadcasts.contains(actionName);
- }
- }
-
- @Override
- public int checkSignatures(@NonNull String pkg1, @NonNull String pkg2) {
- return mComputer.checkSignatures(pkg1, pkg2);
- }
-
- @Override
- public int checkUidSignatures(int uid1, int uid2) {
- return mComputer.checkUidSignatures(uid1, uid2);
- }
-
- @Override
- public boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate,
- @PackageManager.CertificateInputType int type) {
- return mComputer.hasSigningCertificate(packageName, certificate, type);
- }
-
- @Override
- public boolean hasUidSigningCertificate(int uid, @NonNull byte[] certificate,
- @PackageManager.CertificateInputType int type) {
- return mComputer.hasUidSigningCertificate(uid, certificate, type);
- }
-
- @Override
- public List<String> getAllPackages() {
- return mComputer.getAllPackages();
- }
-
- /**
- * <em>IMPORTANT:</em> Not all packages returned by this method may be known
- * to the system. There are two conditions in which this may occur:
- * <ol>
- * <li>The package is on adoptable storage and the device has been removed</li>
- * <li>The package is being removed and the internal structures are partially updated</li>
- * </ol>
- * The second is an artifact of the current data structures and should be fixed. See
- * b/111075456 for one such instance.
- * This binder API is cached. If the algorithm in this method changes,
- * or if the underlying objecs (as returned by getSettingLPr()) change
- * then the logic that invalidates the cache must be revisited. See
- * calls to invalidateGetPackagesForUidCache() to locate the points at
- * which the cache is invalidated.
- */
- @Override
- public String[] getPackagesForUid(int uid) {
- final int callingUid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(uid);
- enforceCrossUserOrProfilePermission(callingUid, userId,
- /* requireFullPermission */ false,
- /* checkShell */ false, "getPackagesForUid");
- return mComputer.getPackagesForUid(uid);
- }
-
- @Nullable
- @Override
- public String getNameForUid(int uid) {
- return mComputer.getNameForUid(uid);
- }
-
- @Nullable
- @Override
- public String[] getNamesForUids(@NonNull int[] uids) {
- return mComputer.getNamesForUids(uids);
- }
-
- @Override
- public int getUidForSharedUser(@NonNull String sharedUserName) {
- return mComputer.getUidForSharedUser(sharedUserName);
- }
-
- @Override
- public int getFlagsForUid(int uid) {
- return mComputer.getFlagsForUid(uid);
- }
-
- @Override
- public int getPrivateFlagsForUid(int uid) {
- return mComputer.getPrivateFlagsForUid(uid);
- }
-
- @Override
- public boolean isUidPrivileged(int uid) {
- return mComputer.isUidPrivileged(uid);
- }
-
- // NOTE: Can't remove due to unsupported app usage
- @NonNull
- @Override
- public String[] getAppOpPermissionPackages(@NonNull String permissionName) {
- return mComputer.getAppOpPermissionPackages(permissionName);
- }
-
- @Override
- public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(), intent, resolvedType,
- flags, 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
- }
-
- @Override
- public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
- return mPreferredActivityHelper.findPersistentPreferredActivity(intent, userId);
- }
-
- @Override
- public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
- IntentFilter filter, int match, ComponentName activity) {
- mPreferredActivityHelper.setLastChosenActivity(intent, resolvedType, flags,
- new WatchedIntentFilter(filter), match, activity);
- }
-
- @Override
- public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
- return mPreferredActivityHelper.getLastChosenActivity(intent, resolvedType, flags);
- }
-
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
@Nullable String callingFeatureId, boolean isRequesterInstantApp,
@@ -3389,38 +2894,6 @@ public class PackageManagerService extends IPackageManager.Stub
removeMatches, debug, userId, queryMayBeFiltered);
}
- /*
- * Returns if intent can be forwarded from the sourceUserId to the targetUserId
- */
- @Override
- public boolean canForwardTo(@NonNull Intent intent, @Nullable String resolvedType,
- @UserIdInt int sourceUserId, @UserIdInt int targetUserId) {
- return mComputer.canForwardTo(intent, resolvedType, sourceUserId, targetUserId);
- }
-
- private UserInfo getProfileParent(int userId) {
- return mComputer.getProfileParent(userId);
- }
-
- private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
- String resolvedType, int userId) {
- return mComputer.getMatchingCrossProfileIntentFilters(intent,
- resolvedType, userId);
- }
-
- @Override
- public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
-
- return new ParceledListSlice<>(snapshotComputer().queryIntentActivitiesInternal(intent,
- resolvedType, flags, userId));
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
/**
* Returns the package name of the calling Uid if it's an instant app. If it isn't
* instant, returns {@code null}.
@@ -3429,36 +2902,11 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.getInstantAppPackageName(callingUid);
}
- @Override
- public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
- Intent[] specifics, String[] specificTypes, Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
- snapshotComputer(), caller, specifics, specificTypes, intent, resolvedType, flags,
- userId));
- }
-
- @Override
- public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(@NonNull Computer snapshot,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
+ @UserIdInt int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
- snapshotComputer(), intent, resolvedType, flags, userId, Binder.getCallingUid()));
- }
-
- @Override
- public ResolveInfo resolveService(Intent intent, String resolvedType,
- @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- final int callingUid = Binder.getCallingUid();
- return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent, resolvedType,
- flags, userId, callingUid);
- }
-
- @Override
- public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- final int callingUid = Binder.getCallingUid();
- return new ParceledListSlice<>(queryIntentServicesInternal(
- intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
+ snapshot, intent, resolvedType, flags, userId, Binder.getCallingUid()));
}
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
@@ -3469,163 +2917,16 @@ public class PackageManagerService extends IPackageManager.Stub
includeInstantApps);
}
- @Override
- public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
- String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
- snapshotComputer(), intent, resolvedType, flags, userId));
- }
-
- @Override
- public ParceledListSlice<PackageInfo> getInstalledPackages(
- @PackageManager.PackageInfoFlagsBits long flags, int userId) {
- return mComputer.getInstalledPackages(flags, userId);
- }
-
- @Override
- public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
- @UserIdInt int userId) {
- return mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
- }
-
- @Override
- public ParceledListSlice<ApplicationInfo> getInstalledApplications(
- @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
- final int callingUid = Binder.getCallingUid();
- return new ParceledListSlice<>(
- mComputer.getInstalledApplications(flags, userId, callingUid));
- }
-
- @Override
- public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
- if (HIDE_EPHEMERAL_APIS) {
- return null;
- }
- if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
- "getEphemeralApplications");
- }
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
- false /* checkShell */, "getEphemeralApplications");
-
- Computer computer = snapshotComputer();
- List<InstantAppInfo> instantApps = mInstantAppRegistry.getInstantApps(computer, userId);
- if (instantApps != null) {
- return new ParceledListSlice<>(instantApps);
- }
- return null;
- }
-
- @Override
- public boolean isInstantApp(String packageName, int userId) {
- return mComputer.isInstantApp(packageName, userId);
- }
-
private boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
return mComputer.isInstantAppInternal(packageName, userId,
callingUid);
}
- @Override
- public byte[] getInstantAppCookie(String packageName, int userId) {
- if (HIDE_EPHEMERAL_APIS) {
- return null;
- }
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
- false /* checkShell */, "getInstantAppCookie");
- if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
- return null;
- }
- PackageStateInternal packageState = getPackageStateInternal(packageName);
- if (packageState == null || packageState.getPkg() == null) {
- return null;
- }
- return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId);
- }
-
- @Override
- public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
- if (HIDE_EPHEMERAL_APIS) {
- return true;
- }
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
- true /* checkShell */, "setInstantAppCookie");
- if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
- return false;
- }
-
- PackageStateInternal packageState = getPackageStateInternal(packageName);
- if (packageState == null || packageState.getPkg() == null) {
- return false;
- }
- return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie,
- mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId);
- }
-
- @Override
- public Bitmap getInstantAppIcon(String packageName, int userId) {
- if (HIDE_EPHEMERAL_APIS) {
- return null;
- }
-
- if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
- "getInstantAppIcon");
- }
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
- false /* checkShell */, "getInstantAppIcon");
-
- return mInstantAppRegistry.getInstantAppIcon(packageName, userId);
- }
-
boolean isCallerSameApp(String packageName, int uid) {
return mComputer.isCallerSameApp(packageName, uid);
}
- @Override
- public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return ParceledListSlice.emptyList();
- }
- return new ParceledListSlice<>(mComputer.getPersistentApplications(mSafeMode, flags));
- }
-
- @Override
- public ProviderInfo resolveContentProvider(String name,
- @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
- return mComputer.resolveContentProvider(name, flags, userId, Binder.getCallingUid());
- }
-
- @Deprecated
- public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
- mComputer.querySyncProviders(mSafeMode, outNames, outInfo);
- }
-
- @NonNull
- @Override
- public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, @PackageManager.ComponentInfoFlagsBits long flags,
- @Nullable String metaDataKey) {
- return mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
- }
-
- @Nullable
- @Override
- public InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName component, int flags) {
- return mComputer.getInstrumentationInfo(component, flags);
- }
-
- @NonNull
- @Override
- public ParceledListSlice<InstrumentationInfo> queryInstrumentation(
- @NonNull String targetPackage, int flags) {
- return mComputer.queryInstrumentation(targetPackage, flags);
- }
-
public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
@@ -3675,7 +2976,6 @@ public class PackageManagerService extends IPackageManager.Stub
requireFullPermission, checkShell, message);
}
- @Override
public void performFstrimIfNeeded() {
PackageManagerServiceUtils.enforceSystemOrRoot("Only the system can request fstrim");
@@ -3717,28 +3017,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
public void updatePackagesIfNeeded() {
mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
}
- @Override
- public void notifyPackageUse(String packageName, int reason) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- Computer computer = snapshotComputer();
- final boolean notify;
- if (getInstantAppPackageName(callingUid) != null) {
- notify = isCallerSameApp(packageName, callingUid);
- } else {
- notify = !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
- }
- if (!notify) {
- return;
- }
-
- notifyPackageUseInternal(packageName, reason);
- }
private void notifyPackageUseInternal(String packageName, int reason) {
long time = System.currentTimeMillis();
@@ -3747,103 +3029,6 @@ public class PackageManagerService extends IPackageManager.Stub
});
}
- @Override
- public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
- String loaderIsa) {
- int callingUid = Binder.getCallingUid();
- if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && callingUid != Process.SYSTEM_UID) {
- Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
- + callingUid);
- // Do not record dex loads from processes pretending to be system server.
- // Only the system server should be assigned the package "android", so reject calls
- // that don't satisfy the constraint.
- //
- // notifyDexLoad is a PM API callable from the app process. So in theory, apps could
- // craft calls to this API and pretend to be system server. Doing so poses no particular
- // danger for dex load reporting or later dexopt, however it is a sensible check to do
- // in order to verify the expectations.
- return;
- }
-
- int userId = UserHandle.getCallingUserId();
- ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
- if (ai == null) {
- Slog.w(TAG, "Loading a package that does not exist for the calling user. package="
- + loadingPackageName + ", user=" + userId);
- return;
- }
- mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId,
- Process.isIsolated(callingUid));
- }
-
- @Override
- public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
- IDexModuleRegisterCallback callback) {
- int userId = UserHandle.getCallingUserId();
- ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId);
- DexManager.RegisterDexModuleResult result;
- if (ai == null) {
- Slog.w(TAG, "Registering a dex module for a package that does not exist for the" +
- " calling user. package=" + packageName + ", user=" + userId);
- result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
- } else {
- result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
- }
-
- if (callback != null) {
- mHandler.post(() -> {
- try {
- callback.onDexModuleRegistered(dexModulePath, result.success, result.message);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e);
- }
- });
- }
- }
-
- /**
- * Ask the package manager to perform a dex-opt with the given compiler filter.
- *
- * Note: exposed only for the shell command to allow moving packages explicitly to a
- * definite state.
- */
- @Override
- public boolean performDexOptMode(String packageName,
- boolean checkProfiles, String targetCompilerFilter, boolean force,
- boolean bootComplete, String splitName) {
- return mDexOptHelper.performDexOptMode(packageName, checkProfiles, targetCompilerFilter,
- force, bootComplete, splitName);
- }
-
- /**
- * Ask the package manager to perform a dex-opt with the given compiler filter on the
- * secondary dex files belonging to the given package.
- *
- * Note: exposed only for the shell command to allow moving packages explicitly to a
- * definite state.
- */
- @Override
- public boolean performDexOptSecondary(String packageName, String compilerFilter,
- boolean force) {
- return mDexOptHelper.performDexOptSecondary(packageName, compilerFilter, force);
- }
-
- /**
- * Reconcile the information we have about the secondary dex files belonging to
- * {@code packageName} and the actual dex files. For all dex files that were
- * deleted, update the internal records and delete the generated oat files.
- */
- @Override
- public void reconcileSecondaryDexFiles(String packageName) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return;
- } else if (isInstantAppInternal(
- packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
- return;
- }
- mDexManager.reconcileSecondaryDexFiles(packageName);
- }
-
/*package*/ DexManager getDexManager() {
return mDexManager;
}
@@ -3874,67 +3059,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public void dumpProfiles(String packageName) {
- /* Only the shell, root, or the app user should be able to dump profiles. */
- final int callingUid = Binder.getCallingUid();
- final String[] callerPackageNames = getPackagesForUid(callingUid);
- if (callingUid != Process.SHELL_UID
- && callingUid != Process.ROOT_UID
- && !ArrayUtils.contains(callerPackageNames, packageName)) {
- throw new SecurityException("dumpProfiles");
- }
-
- AndroidPackage pkg = getPackage(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- synchronized (mInstallLock) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
- mArtManagerService.dumpProfiles(pkg);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- @Override
- public void forceDexOpt(String packageName) {
- mDexOptHelper.forceDexOpt(packageName);
- }
-
int[] resolveUserIds(int userId) {
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
- @Override
- public Property getProperty(String propertyName, String packageName, String className) {
- Objects.requireNonNull(propertyName);
- Objects.requireNonNull(packageName);
- PackageStateInternal packageState = mComputer.getPackageStateFiltered(packageName,
- Binder.getCallingUid(), UserHandle.getCallingUserId());
- if (packageState == null) {
- return null;
- }
- return mPackageProperty.getProperty(propertyName, packageName, className);
- }
-
- @Override
- public ParceledListSlice<Property> queryProperty(
- String propertyName, @PropertyLocation int componentType) {
- Objects.requireNonNull(propertyName);
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getCallingUserId();
- final List<Property> result =
- mPackageProperty.queryProperty(propertyName, componentType, packageName -> {
- final PackageStateInternal ps = getPackageStateInternal(packageName);
- return shouldFilterApplication(ps, callingUid, callingUserId);
- });
- if (result == null) {
- return ParceledListSlice.emptyList();
- }
- return new ParceledListSlice<>(result);
- }
-
private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
if (installerActivity == null) {
if (DEBUG_INSTANT) {
@@ -4054,159 +3182,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
- int userId) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
-
- if (hidden && isPackageDeviceAdmin(packageName, userId)) {
- Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
- return false;
- }
-
- // Do not allow "android" is being disabled
- if ("android".equals(packageName)) {
- Slog.w(TAG, "Cannot hide package: android");
- return false;
- }
-
- final long callingId = Binder.clearCallingIdentity();
- try {
- final PackageStateInternal packageState =
- mComputer.getPackageStateFiltered(packageName, callingUid, userId);
- if (packageState == null) {
- return false;
- }
-
- // Cannot hide static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- AndroidPackage pkg = packageState.getPkg();
- if (pkg != null) {
- // Cannot hide SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing SDK library: "
- + pkg.getSdkLibName());
- return false;
- }
- // Cannot hide static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- return false;
- }
- }
- // Only allow protected packages to hide themselves.
- if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId())
- && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- Slog.w(TAG, "Not hiding protected package: " + packageName);
- return false;
- }
-
- if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
- return false;
- }
-
- commitPackageStateMutation(null, packageName, packageState1 ->
- packageState1.userState(userId).setHidden(hidden));
-
- final PackageStateInternal newPackageState = getPackageStateInternal(packageName);
-
- if (hidden) {
- killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg");
- sendApplicationHiddenForUser(packageName, newPackageState, userId);
- } else {
- sendPackageAddedForUser(packageName, newPackageState, userId, DataLoaderType.NONE);
- }
-
- scheduleWritePackageRestrictions(userId);
- return true;
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- @Override
- public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
- final int callingUid = Binder.getCallingUid();
- final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
- || callingUid == Process.SYSTEM_UID;
- if (!calledFromSystemOrPhone) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
- "setSystemAppHiddenUntilInstalled");
- }
-
- final PackageStateInternal stateRead = getPackageStateInternal(packageName);
- if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) {
- return;
- }
- if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
- throw new SecurityException("Only system or phone callers can modify core apps");
- }
-
- commitPackageStateMutation(null, mutator -> {
- mutator.forPackage(packageName)
- .setHiddenUntilInstalled(hidden);
- mutator.forDisabledSystemPackage(packageName)
- .setHiddenUntilInstalled(hidden);
- });
- }
-
- @Override
- public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
- final int callingUid = Binder.getCallingUid();
- final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
- || callingUid == Process.SYSTEM_UID;
- if (!calledFromSystemOrPhone) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
- "setSystemAppHiddenUntilInstalled");
- }
-
- final PackageStateInternal packageState = getPackageStateInternal(packageName);
- // The target app should always be in system
- if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
- return false;
- }
- if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
- throw new SecurityException("Only system or phone callers can modify core apps");
- }
- // Check if the install state is the same
- if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
- return false;
- }
-
- final long callingId = Binder.clearCallingIdentity();
- try {
- if (installed) {
- // install the app from uninstalled state
- installExistingPackageAsUser(
- packageName,
- userId,
- PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
- PackageManager.INSTALL_REASON_DEVICE_SETUP,
- null);
- return true;
- }
-
- // uninstall the app from installed state
- deletePackageVersioned(
- new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
- new LegacyPackageDeleteObserver(null).getBinder(),
- userId,
- PackageManager.DELETE_SYSTEM_APP);
- return true;
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
@@ -4218,26 +3193,6 @@ public class PackageManagerService extends IPackageManager.Stub
info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
}
- /**
- * Returns true if application is not found or there was an error. Otherwise it returns
- * the hidden state of the package for the given user.
- */
- @Override
- public boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
- @UserIdInt int userId) {
- return mComputer.getApplicationHiddenSettingAsUser(packageName, userId);
- }
-
- /**
- * @hide
- */
- @Override
- public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
- int installReason, List<String> whiteListedPermissions) {
- return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
- installReason, whiteListedPermissions, null);
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -4247,77 +3202,6 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
- @Override
- public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
- int restrictionFlags, int userId) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
- "setDistractingPackageRestrictionsAsUser");
-
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
- && UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
- + userId);
- }
- Objects.requireNonNull(packageNames, "packageNames cannot be null");
- if (restrictionFlags != 0
- && !mSuspendPackageHelper.isSuspendAllowedForUser(userId, callingUid)) {
- Slog.w(TAG, "Cannot restrict packages due to restrictions on user " + userId);
- return packageNames;
- }
-
- final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
- final IntArray changedUids = new IntArray(packageNames.length);
- final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-
- ArraySet<String> changesToCommit = new ArraySet<>();
- Computer computer = snapshotComputer();
- final boolean[] canRestrict = (restrictionFlags != 0)
- ? mSuspendPackageHelper.canSuspendPackageForUser(computer, packageNames, userId,
- callingUid) : null;
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- final PackageStateInternal packageState =
- computer.getPackageStateInternal(packageName);
- if (packageState == null
- || computer.shouldFilterApplication(packageState, callingUid, userId)) {
- Slog.w(TAG, "Could not find package setting for package: " + packageName
- + ". Skipping...");
- unactionedPackages.add(packageName);
- continue;
- }
- if (canRestrict != null && !canRestrict[i]) {
- unactionedPackages.add(packageName);
- continue;
- }
- final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
- .getDistractionFlags();
- if (restrictionFlags != oldDistractionFlags) {
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
- changesToCommit.add(packageName);
- }
- }
-
- commitPackageStateMutation(null, mutator -> {
- final int size = changesToCommit.size();
- for (int index = 0; index < size; index++) {
- mutator.forPackage(changesToCommit.valueAt(index))
- .userState(userId)
- .setDistractionFlags(restrictionFlags);
- }
- });
-
- if (!changedPackagesList.isEmpty()) {
- final String[] changedPackages = changedPackagesList.toArray(
- new String[changedPackagesList.size()]);
- mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
- changedPackages, changedUids.toArray(), userId, restrictionFlags));
- scheduleWritePackageRestrictions(userId);
- }
- return unactionedPackages.toArray(new String[0]);
- }
-
private void enforceCanSetPackagesSuspendedAsUser(String callingPackage, int callingUid,
int userId, String callingMethod) {
if (callingUid == Process.ROOT_UID
@@ -4328,7 +3212,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
if (ownerPackage != null) {
- final int ownerUid = getPackageUid(ownerPackage, 0, userId);
+ final int ownerUid = mIPackageManager.getPackageUid(ownerPackage, 0, userId);
if (ownerUid == callingUid) {
return;
}
@@ -4337,7 +3221,7 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
callingMethod);
- final int packageUid = getPackageUid(callingPackage, 0, userId);
+ final int packageUid = mIPackageManager.getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
// TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
final boolean allowedShell = callingUid == SHELL_UID
@@ -4349,34 +3233,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
- PersistableBundle appExtras, PersistableBundle launcherExtras,
- SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
- final int callingUid = Binder.getCallingUid();
- enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
- "setPackagesSuspendedAsUser");
- return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
- suspended, appExtras, launcherExtras, dialogInfo, callingPackage, userId,
- callingUid);
- }
-
- @Override
- public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (getPackageUid(packageName, 0, userId) != callingUid) {
- throw new SecurityException("Calling package " + packageName
- + " does not belong to calling uid " + callingUid);
- }
- return mSuspendPackageHelper.getSuspendedPackageAppExtras(
- packageName, userId, callingUid);
- }
-
- @Override
- public boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) {
- return mComputer.isPackageSuspendedForUser(packageName, userId);
- }
-
void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
@UserIdInt int userId) {
// TODO: This can be replaced by a special parameter to iterate all packages, rather than
@@ -4431,67 +3287,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
- Objects.requireNonNull(packageNames, "packageNames cannot be null");
- mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
- "getUnsuspendablePackagesForUser");
- final int callingUid = Binder.getCallingUid();
- if (UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Calling uid " + callingUid
- + " cannot query getUnsuspendablePackagesForUser for user " + userId);
- }
- return mSuspendPackageHelper.getUnsuspendablePackagesForUser(snapshotComputer(),
- packageNames, userId, callingUid);
- }
-
- @Override
- public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can verify applications");
- final int callingUid = Binder.getCallingUid();
-
- final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCode, callingUid);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessage(msg);
- }
-
- @Override
- public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
- long millisecondsToDelay) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can extend verification timeouts");
- final int callingUid = Binder.getCallingUid();
-
- mHandler.post(() -> {
- final PackageVerificationState state = mPendingVerification.get(id);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, callingUid);
-
- long delay = millisecondsToDelay;
- if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
- delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
- }
- if (delay < 0) {
- delay = 0;
- }
-
- if ((state != null) && !state.timeoutExtended()) {
- state.extendTimeout();
-
- final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessageDelayed(msg, delay);
- }
- });
- }
-
private void setEnableRollbackCode(int token, int enableRollbackCode) {
final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS);
msg.arg1 = token;
@@ -4499,223 +3294,6 @@ public class PackageManagerService extends IPackageManager.Stub
mHandler.sendMessage(msg);
}
- @Override
- public void finishPackageInstall(int token, boolean didLaunch) {
- PackageManagerServiceUtils.enforceSystemOrRoot(
- "Only the system is allowed to finish installs");
-
- if (DEBUG_INSTALL) {
- Slog.v(TAG, "BM finishing package install for " + token);
- }
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
-
- final Message msg = mHandler.obtainMessage(POST_INSTALL, token, didLaunch ? 1 : 0);
- mHandler.sendMessage(msg);
- }
-
- @Deprecated
- @Override
- public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
- DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection,
- id, verificationCode, failedDomains, Binder.getCallingUid());
- }
-
- @Deprecated
- @Override
- public int getIntentVerificationStatus(String packageName, int userId) {
- return mDomainVerificationManager.getLegacyState(packageName, userId);
- }
-
- @Deprecated
- @Override
- public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
- return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
- }
-
- @Deprecated
- @Override
- public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
- String packageName) {
- return ParceledListSlice.emptyList();
- }
-
- @NonNull
- @Override
- public ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName) {
- return mComputer.getAllIntentFilters(packageName);
- }
-
- @Override
- public void setInstallerPackageName(String targetPackage, String installerPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- final FunctionalUtils.ThrowingCheckedFunction<Computer, Boolean, RuntimeException>
- implementation = computer -> {
- if (computer.getInstantAppPackageName(callingUid) != null) {
- return false;
- }
-
- PackageStateInternal targetPackageState =
- computer.getPackageStateInternal(targetPackage);
- if (targetPackageState == null
- || computer.shouldFilterApplication(targetPackageState, callingUid,
- callingUserId)) {
- throw new IllegalArgumentException("Unknown target package: " + targetPackage);
- }
-
- PackageStateInternal installerPackageState = null;
- if (installerPackageName != null) {
- installerPackageState = computer.getPackageStateInternal(installerPackageName);
- if (installerPackageState == null
- || shouldFilterApplication(
- installerPackageState, callingUid, callingUserId)) {
- throw new IllegalArgumentException("Unknown installer package: "
- + installerPackageName);
- }
- }
-
- Signature[] callerSignature;
- final int appId = UserHandle.getAppId(callingUid);
- Pair<PackageStateInternal, SharedUserApi> either =
- computer.getPackageOrSharedUser(appId);
- if (either != null) {
- if (either.first != null) {
- callerSignature = either.first.getSigningDetails().getSignatures();
- } else {
- callerSignature = either.second.getSigningDetails().getSignatures();
- }
- } else {
- throw new SecurityException("Unknown calling UID: " + callingUid);
- }
-
- // Verify: can't set installerPackageName to a package that is
- // not signed with the same cert as the caller.
- if (installerPackageState != null) {
- if (compareSignatures(callerSignature,
- installerPackageState.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException(
- "Caller does not have same cert as new installer package "
- + installerPackageName);
- }
- }
-
- // Verify: if target already has an installer package, it must
- // be signed with the same cert as the caller.
- String targetInstallerPackageName =
- targetPackageState.getInstallSource().installerPackageName;
- PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null
- ? null : computer.getPackageStateInternal(targetInstallerPackageName);
-
- if (targetInstallerPkgSetting != null) {
- if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException(
- "Caller does not have same cert as old installer package "
- + targetInstallerPackageName);
- }
- } else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
- != PERMISSION_GRANTED) {
- // This is probably an attempt to exploit vulnerability b/150857253 of taking
- // privileged installer permissions when the installer has been uninstalled or
- // was never set.
- EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
-
- final long binderToken = Binder.clearCallingIdentity();
- try {
- if (mInjector.getCompatibility().isChangeEnabledByUid(
- THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
- callingUid)) {
- throw new SecurityException("Neither user " + callingUid
- + " nor current process has "
- + Manifest.permission.INSTALL_PACKAGES);
- } else {
- // If change disabled, fail silently for backwards compatibility
- return false;
- }
- } finally {
- Binder.restoreCallingIdentity(binderToken);
- }
- }
-
- return true;
- };
- PackageStateMutator.InitialState initialState = recordInitialState();
- boolean allowed = implementation.apply(snapshotComputer());
- if (allowed) {
- // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
- // should find an alternative which avoids any race conditions
- PackageStateInternal targetPackageState;
- synchronized (mLock) {
- PackageStateMutator.Result result = commitPackageStateMutation(initialState,
- targetPackage, state -> state.setInstaller(installerPackageName));
- if (result.isPackagesChanged() || result.isStateChanged()) {
- synchronized (mPackageStateWriteLock) {
- allowed = implementation.apply(snapshotComputer());
- if (allowed) {
- commitPackageStateMutation(null, targetPackage,
- state -> state.setInstaller(installerPackageName));
- } else {
- return;
- }
- }
- }
- targetPackageState = getPackageStateInternal(targetPackage);
- mSettings.addInstallerPackageNames(targetPackageState.getInstallSource());
- }
- mAppsFilter.addPackage(targetPackageState);
- scheduleWriteSettings();
- }
- }
-
- @Override
- public void setApplicationCategoryHint(String packageName, int categoryHint,
- String callerPackageName) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- throw new SecurityException("Instant applications don't have access to this method");
- }
- mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
- callerPackageName);
-
- final PackageStateMutator.InitialState initialState = recordInitialState();
-
- final FunctionalUtils.ThrowingFunction<Computer, PackageStateMutator.Result>
- implementation = computer -> {
- PackageStateInternal packageState = computer.getPackageStateFiltered(packageName,
- Binder.getCallingUid(), UserHandle.getCallingUserId());
- if (packageState == null) {
- throw new IllegalArgumentException("Unknown target package " + packageName);
- }
-
- if (!Objects.equals(callerPackageName,
- packageState.getInstallSource().installerPackageName)) {
- throw new IllegalArgumentException("Calling package " + callerPackageName
- + " is not installer for " + packageName);
- }
-
- if (packageState.getCategoryOverride() != categoryHint) {
- return commitPackageStateMutation(initialState,
- packageName, state -> state.setCategoryOverride(categoryHint));
- } else {
- return null;
- }
- };
-
- PackageStateMutator.Result result = implementation.apply(snapshotComputer());
- if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) {
- // TODO: Specific return value of what state changed?
- // The installer on record might have changed, retry with lock
- synchronized (mPackageStateWriteLock) {
- result = implementation.apply(snapshotComputer());
- }
- }
-
- if (result != null && result.isCommitted()) {
- scheduleWriteSettings();
- }
- }
-
/**
* Callback from PackageSettings whenever an app is first transitioned out of the
* 'stopped' state. Normally we just issue the broadcast, but we can't do that if
@@ -4794,21 +3372,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public void deletePackageAsUser(String packageName, int versionCode,
- IPackageDeleteObserver observer, int userId, int flags) {
- deletePackageVersioned(new VersionedPackage(packageName, versionCode),
- new LegacyPackageDeleteObserver(observer).getBinder(), userId, flags);
- }
-
- @Override
public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId) {
mDeletePackageHelper.deleteExistingPackageAsUser(
versionedPackage, observer, userId);
}
- @Override
public void deletePackageVersioned(VersionedPackage versionedPackage,
final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
mDeletePackageHelper.deletePackageVersionedInternal(
@@ -4825,15 +3394,14 @@ public class PackageManagerService extends IPackageManager.Stub
boolean isCallerVerifier(int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
- return mRequiredVerifierPackage != null &&
- callingUid == getPackageUid(mRequiredVerifierPackage, 0, callingUserId);
+ return mRequiredVerifierPackage != null && callingUid == mIPackageManager.getPackageUid(
+ mRequiredVerifierPackage, 0, callingUserId);
}
- @Override
public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
final int callingUid = Binder.getCallingUid();
- if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
- != PERMISSION_GRANTED) {
+ if (mIPackageManager.checkUidPermission(android.Manifest.permission.MANAGE_USERS,
+ callingUid) != PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "128599183", -1, "");
throw new SecurityException(android.Manifest.permission.MANAGE_USERS
+ " permission is required to call this API");
@@ -4888,139 +3456,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mDevicePolicyManager;
}
- @Override
- public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
- int userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.DELETE_PACKAGES, null);
- PackageStateInternal packageState = getPackageStateInternal(packageName);
- if (packageState != null && packageState.getPkg() != null) {
- AndroidPackage pkg = packageState.getPkg();
- // Cannot block uninstall SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing SDK library: " + pkg.getSdkLibName());
- return false;
- }
- // Cannot block uninstall of static shared libs as they are
- // considered a part of the using app (emulating static linking).
- // Also static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing static shared library: " + pkg.getStaticSharedLibName());
- return false;
- }
- }
- synchronized (mLock) {
- mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
- }
-
- scheduleWritePackageRestrictions(userId);
- return true;
- }
-
- @Override
- public boolean getBlockUninstallForUser(@NonNull String packageName, @UserIdInt int userId) {
- return mComputer.getBlockUninstallForUser(packageName, userId);
- }
-
- @Override
- public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) {
- PackageManagerServiceUtils.enforceSystemOrRoot(
- "setRequiredForSystemUser can only be run by the system or root");
-
- PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
- packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser));
- if (!result.isCommitted()) {
- return false;
- }
-
- scheduleWriteSettings();
- return true;
- }
-
- @Override
- public void clearApplicationProfileData(String packageName) {
- PackageManagerServiceUtils.enforceSystemOrRoot(
- "Only the system can clear all profile data");
-
- final AndroidPackage pkg = getPackage(packageName);
- try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
- synchronized (mInstallLock) {
- mAppDataHelper.clearAppProfilesLIF(pkg);
- }
- }
- }
-
- @Override
- public void clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer, final int userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CLEAR_APP_USER_DATA, null);
-
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- false /* checkShell */, "clear application data");
-
- if (mComputer.getPackageStateFiltered(packageName, callingUid, userId) == null) {
- if (observer != null) {
- mHandler.post(() -> {
- try {
- observer.onRemoveCompleted(packageName, false);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- });
- }
- return;
- }
- if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
- throw new SecurityException("Cannot clear data for a protected package: "
- + packageName);
- }
-
- // Queue up an async operation since the package deletion may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- final boolean succeeded;
- try (PackageFreezer freezer = freezePackage(packageName,
- "clearApplicationUserData")) {
- synchronized (mInstallLock) {
- succeeded = clearApplicationUserDataLIF(packageName, userId);
- }
- mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId);
- synchronized (mLock) {
- if (succeeded) {
- resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
- }
- }
- }
- if (succeeded) {
- // invoke DeviceStorageMonitor's update method to clear any notifications
- DeviceStorageMonitorInternal dsm = LocalServices
- .getService(DeviceStorageMonitorInternal.class);
- if (dsm != null) {
- dsm.checkMemory();
- }
- if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
- == PERMISSION_GRANTED) {
- unsuspendForSuspendingPackage(snapshotComputer(), packageName, userId);
- removeAllDistractingPackageRestrictions(userId);
- flushPackageRestrictionsAsUserInternalLocked(userId);
- }
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(packageName, succeeded);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- } //end if observer
- } //end run
- });
- }
-
private boolean clearApplicationUserDataLIF(String packageName, int userId) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
@@ -5103,106 +3538,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public void deleteApplicationCacheFiles(final String packageName,
- final IPackageDataObserver observer) {
- final int userId = UserHandle.getCallingUserId();
- deleteApplicationCacheFilesAsUser(packageName, userId, observer);
- }
-
- @Override
- public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
- final IPackageDataObserver observer) {
- final int callingUid = Binder.getCallingUid();
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES)
- != PackageManager.PERMISSION_GRANTED) {
- // If the caller has the old delete cache permission, silently ignore. Else throw.
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.DELETE_CACHE_FILES)
- == PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Calling uid " + callingUid + " does not have " +
- android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES +
- ", silently ignoring");
- return;
- }
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
- }
- enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true,
- /* checkShell= */ false, "delete application cache files");
- final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_INSTANT_APPS);
-
- final AndroidPackage pkg = getPackage(packageName);
-
- // Queue up an async operation since the package deletion may take a little while.
- mHandler.post(() -> {
- final PackageStateInternal ps =
- pkg == null ? null : getPackageStateInternal(pkg.getPackageName());
- boolean doClearData = true;
- if (ps != null) {
- final boolean targetIsInstantApp =
- ps.getUserStateOrDefault(UserHandle.getUserId(callingUid)).isInstantApp();
- doClearData = !targetIsInstantApp
- || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
- }
- if (doClearData) {
- synchronized (mInstallLock) {
- final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
- // We're only clearing cache files, so we don't care if the
- // app is unfrozen and still able to run
- mAppDataHelper.clearAppDataLIF(pkg, userId,
- flags | Installer.FLAG_CLEAR_CACHE_ONLY);
- mAppDataHelper.clearAppDataLIF(pkg, userId,
- flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- }
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(packageName, true);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- }
- });
- }
-
- @Override
- public void getPackageSizeInfo(final String packageName, int userId,
- final IPackageStatsObserver observer) {
- throw new UnsupportedOperationException(
- "Shame on you for calling the hidden API getPackageSizeInfo(). Shame!");
- }
-
int getUidTargetSdkVersion(int uid) {
return mComputer.getUidTargetSdkVersion(uid);
}
- @Override
- public void addPreferredActivity(IntentFilter filter, int match,
- ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
- mPreferredActivityHelper.addPreferredActivity(
- new WatchedIntentFilter(filter), match, set, activity, true, userId,
- "Adding preferred", removeExisting);
- }
-
void postPreferredActivityChangedBroadcast(int userId) {
mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
}
- @Override
- public void replacePreferredActivity(IntentFilter filter, int match,
- ComponentName[] set, ComponentName activity, int userId) {
- mPreferredActivityHelper.replacePreferredActivity(new WatchedIntentFilter(filter), match,
- set, activity, userId);
- }
-
- @Override
- public void clearPackagePreferredActivities(String packageName) {
- mPreferredActivityHelper.clearPackagePreferredActivities(packageName);
- }
-
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
@@ -5222,109 +3565,6 @@ public class PackageManagerService extends IPackageManager.Stub
mPreferredActivityHelper.updateDefaultHomeNotLocked(userId);
}
- @Override
- public void resetApplicationPreferences(int userId) {
- mPreferredActivityHelper.resetApplicationPreferences(userId);
- }
-
- @Override
- public int getPreferredActivities(List<IntentFilter> outFilters,
- List<ComponentName> outActivities, String packageName) {
- return mPreferredActivityHelper.getPreferredActivities(outFilters, outActivities,
- packageName, mComputer);
- }
-
- @Override
- public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
- int userId) {
- mPreferredActivityHelper.addPersistentPreferredActivity(new WatchedIntentFilter(filter),
- activity, userId);
- }
-
- @Override
- public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
- mPreferredActivityHelper.clearPackagePersistentPreferredActivities(packageName, userId);
- }
-
- /**
- * Non-Binder method, support for the backup/restore mechanism: write the
- * full set of preferred activities in its canonical XML format. Returns the
- * XML output as a byte array, or null if there is none.
- */
- @Override
- public byte[] getPreferredActivityBackup(int userId) {
- return mPreferredActivityHelper.getPreferredActivityBackup(userId);
- }
-
- @Override
- public void restorePreferredActivities(byte[] backup, int userId) {
- mPreferredActivityHelper.restorePreferredActivities(backup, userId);
- }
-
- /**
- * Non-Binder method, support for the backup/restore mechanism: write the
- * default browser (etc) settings in its canonical XML format. Returns the default
- * browser XML representation as a byte array, or null if there is none.
- */
- @Override
- public byte[] getDefaultAppsBackup(int userId) {
- return mPreferredActivityHelper.getDefaultAppsBackup(userId);
- }
-
- @Override
- public void restoreDefaultApps(byte[] backup, int userId) {
- mPreferredActivityHelper.restoreDefaultApps(backup, userId);
- }
-
- @Override
- public byte[] getDomainVerificationBackup(int userId) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only the system may call getDomainVerificationBackup()");
- }
-
- try {
- try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
- TypedXmlSerializer serializer = Xml.resolveSerializer(output);
- mDomainVerificationManager.writeSettings(snapshotComputer(), serializer, true,
- userId);
- return output.toByteArray();
- }
- } catch (Exception e) {
- if (DEBUG_BACKUP) {
- Slog.e(TAG, "Unable to write domain verification for backup", e);
- }
- return null;
- }
- }
-
- @Override
- public void restoreDomainVerification(byte[] backup, int userId) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only the system may call restorePreferredActivities()");
- }
-
- try {
- ByteArrayInputStream input = new ByteArrayInputStream(backup);
- TypedXmlPullParser parser = Xml.resolvePullParser(input);
-
- // User ID input isn't necessary here as it assumes the user integers match and that
- // the only states inside the backup XML are for the target user.
- mDomainVerificationManager.restoreSettings(snapshotComputer(), parser);
- input.close();
- } catch (Exception e) {
- if (DEBUG_BACKUP) {
- Slog.e(TAG, "Exception restoring domain verification: " + e.getMessage());
- }
- }
- }
-
- @Override
- public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
- int sourceUserId, int targetUserId, int flags) {
- addCrossProfileIntentFilter(new WatchedIntentFilter(intentFilter), ownerPackage,
- sourceUserId, targetUserId, flags);
- }
-
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
@@ -5360,55 +3600,25 @@ public class PackageManagerService extends IPackageManager.Stub
scheduleWritePackageRestrictions(sourceUserId);
}
- @Override
- public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- final int callingUid = Binder.getCallingUid();
- enforceOwnerRights(ownerPackage, callingUid);
- PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
- synchronized (mLock) {
- CrossProfileIntentResolver resolver =
- mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
- ArraySet<CrossProfileIntentFilter> set =
- new ArraySet<>(resolver.filterSet());
- for (CrossProfileIntentFilter filter : set) {
- if (filter.getOwnerPackage().equals(ownerPackage)) {
- resolver.removeFilter(filter);
- }
- }
- }
- scheduleWritePackageRestrictions(sourceUserId);
- }
-
// Enforcing that callingUid is owning pkg on userId
private void enforceOwnerRights(String pkg, int callingUid) {
// The system owns everything.
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return;
}
- final String[] callerPackageNames = getPackagesForUid(callingUid);
+ final String[] callerPackageNames = mIPackageManager.getPackagesForUid(callingUid);
if (!ArrayUtils.contains(callerPackageNames, pkg)) {
throw new SecurityException("Calling uid " + callingUid
+ " does not own package " + pkg);
}
final int callingUserId = UserHandle.getUserId(callingUid);
- PackageInfo pi = getPackageInfo(pkg, 0, callingUserId);
+ PackageInfo pi = mIPackageManager.getPackageInfo(pkg, 0, callingUserId);
if (pi == null) {
throw new IllegalArgumentException("Unknown package " + pkg + " on user "
+ callingUserId);
}
}
- @Override
- public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
- }
-
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
if (ums == null || sessionInfo.isStaged()) {
@@ -5439,11 +3649,6 @@ public class PackageManagerService extends IPackageManager.Stub
userId);
}
- @Override
- public void setHomeActivity(ComponentName comp, int userId) {
- mPreferredActivityHelper.setHomeActivity(comp, userId);
- }
-
private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
@@ -5495,84 +3700,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public String getDefaultTextClassifierPackageName() {
- return ensureSystemPackageName(
- mContext.getString(R.string.config_servicesExtensionPackage));
- }
-
- @Override
- public String getSystemTextClassifierPackageName() {
- return ensureSystemPackageName(
- mContext.getString(R.string.config_defaultTextClassifierPackage));
- }
-
- @Override
- public @Nullable String getAttentionServicePackageName() {
- return ensureSystemPackageName(
- getPackageFromComponentString(R.string.config_defaultAttentionService));
- }
-
- @Override
- public @Nullable String getRotationResolverPackageName() {
- return ensureSystemPackageName(
- getPackageFromComponentString(R.string.config_defaultRotationResolverService));
- }
-
@Nullable
private String getDeviceConfiguratorPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_deviceConfiguratorPackageName));
}
- @Override
- public String getWellbeingPackageName() {
- final long identity = Binder.clearCallingIdentity();
- try {
- return CollectionUtils.firstOrNull(
- mContext.getSystemService(RoleManager.class).getRoleHolders(
- RoleManager.ROLE_SYSTEM_WELLBEING));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public String getAppPredictionServicePackageName() {
- return ensureSystemPackageName(
- getPackageFromComponentString(R.string.config_defaultAppPredictionService));
- }
-
- @Override
- public String getSystemCaptionsServicePackageName() {
- return ensureSystemPackageName(
- getPackageFromComponentString(R.string.config_defaultSystemCaptionsService));
- }
-
- @Override
- public String getSetupWizardPackageName() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Non-system caller");
- }
- return mPmInternal.getSetupWizardPackageName();
- }
-
public @Nullable String getAmbientContextDetectionPackageName() {
return ensureSystemPackageName(getPackageFromComponentString(
R.string.config_defaultAmbientContextDetectionService));
}
- public String getIncidentReportApproverPackageName() {
- return ensureSystemPackageName(mContext.getString(
- R.string.config_incidentReportApproverPackage));
- }
-
- @Override
- public String getContentCaptureServicePackageName() {
- return ensureSystemPackageName(
- getPackageFromComponentString(R.string.config_defaultContentCaptureService));
- }
-
public String getOverlayConfigSignaturePackageName() {
return ensureSystemPackageName(mInjector.getSystemConfig()
.getOverlayConfigSignaturePackage());
@@ -5640,8 +3778,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
final long token = Binder.clearCallingIdentity();
try {
- if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
- PackageInfo packageInfo = getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
+ if (mIPackageManager.getPackageInfo(packageName, MATCH_FACTORY_ONLY,
+ UserHandle.USER_SYSTEM) == null) {
+ PackageInfo packageInfo =
+ mIPackageManager.getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
if (packageInfo != null) {
EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid,
"");
@@ -5654,39 +3794,6 @@ public class PackageManagerService extends IPackageManager.Stub
return packageName;
}
- @Override
- public void setApplicationEnabledSetting(String appPackageName,
- int newState, int flags, int userId, String callingPackage) {
- if (!mUserManager.exists(userId)) return;
- if (callingPackage == null) {
- callingPackage = Integer.toString(Binder.getCallingUid());
- }
-
- setEnabledSettings(List.of(new ComponentEnabledSetting(appPackageName, newState, flags)),
- userId, callingPackage);
- }
-
- @Override
- public void setUpdateAvailable(String packageName, boolean updateAvailable) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
- commitPackageStateMutation(null, packageName, state ->
- state.setUpdateAvailable(updateAvailable));
- }
-
- @Override
- public void overrideLabelAndIcon(@NonNull ComponentName componentName,
- @NonNull String nonLocalizedLabel, int icon, int userId) {
- if (TextUtils.isEmpty(nonLocalizedLabel)) {
- throw new IllegalArgumentException("Override label should be a valid String");
- }
- updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId);
- }
-
- @Override
- public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) {
- updateComponentLabelIcon(componentName, null, null, userId);
- }
-
@VisibleForTesting(visibility = Visibility.PRIVATE)
public void updateComponentLabelIcon(/*@NonNull*/ ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon, int userId) {
@@ -5753,25 +3860,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public void setComponentEnabledSetting(ComponentName componentName,
- int newState, int flags, int userId) {
- if (!mUserManager.exists(userId)) return;
-
- setEnabledSettings(List.of(new ComponentEnabledSetting(componentName, newState, flags)),
- userId, null /* callingPackage */);
- }
-
- @Override
- public void setComponentEnabledSettings(List<ComponentEnabledSetting> settings, int userId) {
- if (!mUserManager.exists(userId)) return;
- if (settings == null || settings.isEmpty()) {
- throw new IllegalArgumentException("The list of enabled settings is empty");
- }
-
- setEnabledSettings(settings, userId, null /* callingPackage */);
- }
-
private void setEnabledSettings(List<ComponentEnabledSetting> settings, int userId,
String callingPackage) {
final int callingUid = Binder.getCallingUid();
@@ -5841,7 +3929,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
final boolean isCallerTargetApp = ArrayUtils.contains(
- getPackagesForUid(callingUid), packageName);
+ mIPackageManager.getPackagesForUid(callingUid), packageName);
final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
// Limit who can change which apps
if (!isCallerTargetApp) {
@@ -6049,8 +4137,8 @@ public class PackageManagerService extends IPackageManager.Stub
pkgSetting.setEnabled(newState, userId, callingPackage);
if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
|| newState == COMPONENT_ENABLED_STATE_DISABLED)
- && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
- == PERMISSION_GRANTED) {
+ && mIPackageManager.checkPermission(Manifest.permission.SUSPEND_APPS,
+ packageName, userId) == PERMISSION_GRANTED) {
// This app should not generally be allowed to get disabled by the UI, but
// if it ever does, we don't want to end up with some of the user's apps
// permanently suspended.
@@ -6093,22 +4181,6 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- @WorkerThread
- @Override
- public void flushPackageRestrictionsAsUser(int userId) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return;
- }
- if (!mUserManager.exists(userId)) {
- return;
- }
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
- false /* checkShell */, "flushPackageRestrictions");
- synchronized (mLock) {
- flushPackageRestrictionsAsUserInternalLocked(userId);
- }
- }
-
@GuardedBy("mLock")
private void flushPackageRestrictionsAsUserInternalLocked(int userId) {
// NOTE: this invokes synchronous disk access, so callers using this
@@ -6141,100 +4213,17 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.getBroadcastAllowList(packageName, userIds, isInstantApp);
}
- @Override
- public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
- if (!mUserManager.exists(userId)) return;
- final int callingUid = Binder.getCallingUid();
- final Computer computer = snapshotComputer();
- if (computer.getInstantAppPackageName(callingUid) == null) {
- final int permission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- if (!allowedByPermission
- && !ArrayUtils.contains(computer.getPackagesForUid(callingUid), packageName)) {
- throw new SecurityException(
- "Permission Denial: attempt to change stopped state from pid="
- + Binder.getCallingPid()
- + ", uid=" + callingUid + ", package=" + packageName);
- }
- computer.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */, "stop package");
-
- final PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
- final PackageUserState packageUserState = packageState == null
- ? null : packageState.getUserStateOrDefault(userId);
- if (packageState != null
- && !computer.shouldFilterApplication(packageState, callingUid, userId)
- && packageUserState.isStopped() != stopped) {
- boolean wasNotLaunched = packageUserState.isNotLaunched();
- commitPackageStateMutation(null, packageName, state -> {
- PackageUserStateWrite userState = state.userState(userId);
- userState.setStopped(stopped);
- if (wasNotLaunched) {
- userState.setNotLaunched(false);
- }
- });
-
- if (wasNotLaunched) {
- final String installerPackageName =
- packageState.getInstallSource().installerPackageName;
- if (installerPackageName != null) {
- notifyFirstLaunch(packageName, installerPackageName, userId);
- }
- }
-
- scheduleWritePackageRestrictions(userId);
- }
- }
-
- // If this would cause the app to leave force-stop, then also make sure to unhibernate the
- // app if needed.
- if (!stopped) {
- mHandler.post(() -> {
- AppHibernationManagerInternal ah =
- mInjector.getLocalService(AppHibernationManagerInternal.class);
- if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
- ah.setHibernatingForUser(packageName, userId, false);
- ah.setHibernatingGlobally(packageName, false);
- }
- });
- }
- }
-
- @Nullable
- @Override
- public String getInstallerPackageName(@NonNull String packageName) {
- return mComputer.getInstallerPackageName(packageName);
- }
-
- @Override
- @Nullable
- public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) {
- return mComputer.getInstallSourceInfo(packageName);
- }
-
- @PackageManager.EnabledState
- @Override
- public int getApplicationEnabledSetting(@NonNull String packageName, @UserIdInt int userId) {
- return mComputer.getApplicationEnabledSetting(packageName, userId);
- }
-
- @Override
- public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
- return mComputer.getComponentEnabledSetting(component, Binder.getCallingUid(), userId);
- }
-
- @Override
- public void enterSafeMode() {
- PackageManagerServiceUtils.enforceSystemOrRoot(
- "Only the system can request entering safe mode");
-
- if (!mSystemReady) {
- mSafeMode = true;
+ /**
+ * Used by SystemServer
+ */
+ public void waitForAppDataPrepared() {
+ if (mPrepareAppDataFuture == null) {
+ return;
}
+ ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
+ mPrepareAppDataFuture = null;
}
- @Override
public void systemReady() {
PackageManagerServiceUtils.enforceSystemOrRoot(
"Only the system can claim the system is ready");
@@ -6265,7 +4254,7 @@ public class PackageManagerService extends IPackageManager.Stub
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
false, co, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
- .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
mAppsFilter.onSystemReady();
@@ -6397,44 +4386,11 @@ public class PackageManagerService extends IPackageManager.Stub
// Prune unused static shared libraries which have been cached a period of time
schedulePruneUnusedStaticSharedLibraries(false /* delay */);
- }
- /**
- * Used by SystemServer
- */
- public void waitForAppDataPrepared() {
- if (mPrepareAppDataFuture == null) {
- return;
+ // TODO(b/222706900): Remove this intent interceptor before T launch
+ if (mIntentResolverInterceptor != null) {
+ mIntentResolverInterceptor.registerListeners();
}
- ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
- mPrepareAppDataFuture = null;
- }
-
- @Override
- public boolean isSafeMode() {
- // allow instant applications
- return mSafeMode;
- }
-
- @Override
- public boolean hasSystemUidErrors() {
- // allow instant applications
- return false;
- }
-
- @Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
- (new PackageManagerShellCommand(this, mContext,mDomainVerificationManager.getShell()))
- .exec(this, in, out, err, args, callback, resultReceiver);
- }
-
- @SuppressWarnings("resource")
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- new DumpHelper(this).doDump(fd, pw, args);
}
void dumpSnapshotStats(PrintWriter pw, boolean isBrief) {
@@ -6473,9 +4429,9 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
for (String packageName : apkList) {
- setSystemAppHiddenUntilInstalled(packageName, true);
+ mIPackageManager.setSystemAppHiddenUntilInstalled(packageName, true);
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(false)) {
- setSystemAppInstallState(packageName, false, user.id);
+ mIPackageManager.setSystemAppInstallState(packageName, false, user.id);
}
}
}
@@ -6513,98 +4469,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public int movePackage(final String packageName, final String volumeUuid) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
-
- final int callingUid = Binder.getCallingUid();
- final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
- final int moveId = mNextMoveId.getAndIncrement();
- mHandler.post(() -> {
- try {
- MovePackageHelper movePackageHelper = new MovePackageHelper(this);
- movePackageHelper.movePackageInternal(
- packageName, volumeUuid, moveId, callingUid, user);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId, e.error);
- }
- });
- return moveId;
- }
-
- @Override
- public int movePrimaryStorage(String volumeUuid) throws RemoteException {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
-
- final int realMoveId = mNextMoveId.getAndIncrement();
- final Bundle extras = new Bundle();
- extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid);
- mMoveCallbacks.notifyCreated(realMoveId, extras);
-
- final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() {
- @Override
- public void onCreated(int moveId, Bundle extras) {
- // Ignored
- }
-
- @Override
- public void onStatusChanged(int moveId, int status, long estMillis) {
- mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis);
- }
- };
-
- final StorageManager storage = mInjector.getSystemService(StorageManager.class);
- storage.setPrimaryStorageUuid(volumeUuid, callback);
- return realMoveId;
- }
-
- @Override
- public int getMoveStatus(int moveId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
- return mMoveCallbacks.mLastStatus.get(moveId);
- }
-
- @Override
- public void registerMoveCallback(IPackageMoveObserver callback) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
- mMoveCallbacks.register(callback);
- }
-
- @Override
- public void unregisterMoveCallback(IPackageMoveObserver callback) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
- mMoveCallbacks.unregister(callback);
- }
-
- @Override
- public boolean setInstallLocation(int loc) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
- null);
- if (getInstallLocation() == loc) {
- return true;
- }
- if (loc == InstallLocationUtils.APP_INSTALL_AUTO
- || loc == InstallLocationUtils.APP_INSTALL_INTERNAL
- || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
- android.provider.Settings.Global.putInt(mContext.getContentResolver(),
- android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
- return true;
- }
- return false;
- }
-
- @Override
- public int getInstallLocation() {
- // allow instant app access
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
- InstallLocationUtils.APP_INSTALL_AUTO);
- }
-
/** Called by UserManagerService */
void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) {
synchronized (mLock) {
@@ -6664,18 +4528,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can read the verifier device identity");
-
- synchronized (mLock) {
- return mSettings.getVerifierDeviceIdentityLPw(mLiveComputer);
- }
- }
-
- @Override
public boolean isStorageLow() {
// allow instant applications
final long token = Binder.clearCallingIdentity();
@@ -6692,50 +4544,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public IPackageInstaller getPackageInstaller() {
- // Return installer service for internal calls.
- if (PackageManagerServiceUtils.isSystemOrRoot()) {
- return mInstallerService;
- }
- // Return null for InstantApps.
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- return mInstallerService;
- }
-
- @Override
- public IArtManager getArtManager() {
- return mArtManagerService;
- }
-
boolean userNeedsBadging(int userId) {
return mUserNeedsBadging.get(userId);
}
- @Nullable
- @Override
- public KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias) {
- return mComputer.getKeySetByAlias(packageName, alias);
- }
-
- @Nullable
- @Override
- public KeySet getSigningKeySet(@NonNull String packageName) {
- return mComputer.getSigningKeySet(packageName);
- }
-
- @Override
- public boolean isPackageSignedByKeySet(@NonNull String packageName, @NonNull KeySet ks) {
- return mComputer.isPackageSignedByKeySet(packageName, ks);
- }
-
- @Override
- public boolean isPackageSignedByKeySetExactly(@NonNull String packageName, @NonNull KeySet ks) {
- return mComputer.isPackageSignedByKeySetExactly(packageName, ks);
- }
-
private void deletePackageIfUnused(final String packageName) {
PackageStateInternal ps = getPackageStateInternal(packageName);
if (ps == null) {
@@ -6792,6 +4604,2512 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.canQueryPackage(callingUid, targetPackageName);
}
+ void checkPackageStartable(@NonNull Computer snapshot, @NonNull String packageName,
+ @UserIdInt int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (snapshot.getInstantAppPackageName(callingUid) != null) {
+ throw new SecurityException("Instant applications don't have access to this method");
+ }
+ if (!mUserManager.exists(userId)) {
+ throw new SecurityException("User doesn't exist");
+ }
+ snapshot.enforceCrossUserPermission(callingUid, userId, false, false,
+ "checkPackageStartable");
+ switch (snapshot.getPackageStartability(mSafeMode, packageName, callingUid, userId)) {
+ case PACKAGE_STARTABILITY_NOT_FOUND:
+ throw new SecurityException("Package " + packageName + " was not found!");
+ case PACKAGE_STARTABILITY_NOT_SYSTEM:
+ throw new SecurityException("Package " + packageName + " not a system app!");
+ case PACKAGE_STARTABILITY_FROZEN:
+ throw new SecurityException("Package " + packageName + " is currently frozen!");
+ case PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED:
+ throw new SecurityException("Package " + packageName + " is not encryption aware!");
+ case PACKAGE_STARTABILITY_OK:
+ default:
+ }
+ }
+
+ void setPackageStoppedState(@NonNull Computer snapshot, @NonNull String packageName,
+ boolean stopped, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) return;
+ final int callingUid = Binder.getCallingUid();
+ if (snapshot.getInstantAppPackageName(callingUid) == null) {
+ final int permission = mContext.checkCallingOrSelfPermission(
+ Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ if (!allowedByPermission
+ && !ArrayUtils.contains(snapshot.getPackagesForUid(callingUid), packageName)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change stopped state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + callingUid + ", package=" + packageName);
+ }
+ snapshot.enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */, "stop package");
+
+ final PackageStateInternal packageState =
+ snapshot.getPackageStateInternal(packageName);
+ final PackageUserState packageUserState = packageState == null
+ ? null : packageState.getUserStateOrDefault(userId);
+ if (packageState != null
+ && !snapshot.shouldFilterApplication(packageState, callingUid, userId)
+ && packageUserState.isStopped() != stopped) {
+ boolean wasNotLaunched = packageUserState.isNotLaunched();
+ commitPackageStateMutation(null, packageName, state -> {
+ PackageUserStateWrite userState = state.userState(userId);
+ userState.setStopped(stopped);
+ if (wasNotLaunched) {
+ userState.setNotLaunched(false);
+ }
+ });
+
+ if (wasNotLaunched) {
+ final String installerPackageName =
+ packageState.getInstallSource().installerPackageName;
+ if (installerPackageName != null) {
+ notifyFirstLaunch(packageName, installerPackageName, userId);
+ }
+ }
+
+ scheduleWritePackageRestrictions(userId);
+ }
+ }
+
+ // If this would cause the app to leave force-stop, then also make sure to unhibernate the
+ // app if needed.
+ if (!stopped) {
+ mHandler.post(() -> {
+ AppHibernationManagerInternal ah =
+ mInjector.getLocalService(AppHibernationManagerInternal.class);
+ if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
+ ah.setHibernatingForUser(packageName, userId, false);
+ ah.setHibernatingGlobally(packageName, false);
+ }
+ });
+ }
+ }
+
+ public class IPackageManagerImpl extends IPackageManager.Stub {
+
+ @Override
+ public boolean activitySupportsIntent(ComponentName component, Intent intent,
+ String resolvedType) {
+ return mComputer.activitySupportsIntent(mResolveComponentName, component, intent,
+ resolvedType);
+ }
+
+ @Override
+ public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
+ int sourceUserId, int targetUserId, int flags) {
+ PackageManagerService.this.addCrossProfileIntentFilter(
+ new WatchedIntentFilter(intentFilter), ownerPackage, sourceUserId, targetUserId,
+ flags);
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public boolean addPermission(PermissionInfo info) {
+ // Because this is accessed via the package manager service AIDL,
+ // go through the permission manager service AIDL
+ return mContext.getSystemService(PermissionManager.class).addPermission(info, false);
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public boolean addPermissionAsync(PermissionInfo info) {
+ // Because this is accessed via the package manager service AIDL,
+ // go through the permission manager service AIDL
+ return mContext.getSystemService(PermissionManager.class).addPermission(info, true);
+ }
+
+ @Override
+ public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
+ int userId) {
+ mPreferredActivityHelper.addPersistentPreferredActivity(new WatchedIntentFilter(filter),
+ activity, userId);
+ }
+
+ @Override
+ public void addPreferredActivity(IntentFilter filter, int match,
+ ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
+ mPreferredActivityHelper.addPreferredActivity(new WatchedIntentFilter(filter), match,
+ set, activity, true, userId, "Adding preferred", removeExisting);
+ }
+
+ /*
+ * Returns if intent can be forwarded from the sourceUserId to the targetUserId
+ */
+ @Override
+ public boolean canForwardTo(@NonNull Intent intent, @Nullable String resolvedType,
+ @UserIdInt int sourceUserId, @UserIdInt int targetUserId) {
+ return mComputer.canForwardTo(intent, resolvedType, sourceUserId, targetUserId);
+ }
+
+ @Override
+ public boolean canRequestPackageInstalls(String packageName, int userId) {
+ return mComputer.canRequestPackageInstalls(packageName, Binder.getCallingUid(), userId,
+ true /* throwIfPermNotDeclared*/);
+ }
+
+ @Override
+ public String[] canonicalToCurrentPackageNames(String[] names) {
+ return mComputer.canonicalToCurrentPackageNames(names);
+ }
+
+ @Override
+ public void checkPackageStartable(String packageName, int userId) {
+ PackageManagerService.this
+ .checkPackageStartable(snapshotComputer(), packageName, userId);
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public int checkPermission(String permName, String pkgName, int userId) {
+ return PackageManagerService.this.checkPermission(permName, pkgName, userId);
+ }
+
+ @Override
+ public int checkSignatures(@NonNull String pkg1, @NonNull String pkg2) {
+ return mComputer.checkSignatures(pkg1, pkg2);
+ }
+
+ @Override
+ public int checkUidPermission(String permName, int uid) {
+ return mComputer.checkUidPermission(permName, uid);
+ }
+
+ @Override
+ public int checkUidSignatures(int uid1, int uid2) {
+ return mComputer.checkUidSignatures(uid1, uid2);
+ }
+
+ @Override
+ public void clearApplicationProfileData(String packageName) {
+ PackageManagerServiceUtils.enforceSystemOrRoot(
+ "Only the system can clear all profile data");
+
+ final AndroidPackage pkg = getPackage(packageName);
+ try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
+ synchronized (mInstallLock) {
+ mAppDataHelper.clearAppProfilesLIF(pkg);
+ }
+ }
+ }
+
+ @Override
+ public void clearApplicationUserData(final String packageName,
+ final IPackageDataObserver observer, final int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CLEAR_APP_USER_DATA, null);
+
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "clear application data");
+
+ if (mComputer.getPackageStateFiltered(packageName, callingUid, userId) == null) {
+ if (observer != null) {
+ mHandler.post(() -> {
+ try {
+ observer.onRemoveCompleted(packageName, false);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ });
+ }
+ return;
+ }
+ if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+ throw new SecurityException("Cannot clear data for a protected package: "
+ + packageName);
+ }
+
+ // Queue up an async operation since the package deletion may take a little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ mHandler.removeCallbacks(this);
+ final boolean succeeded;
+ try (PackageFreezer freezer = freezePackage(packageName,
+ "clearApplicationUserData")) {
+ synchronized (mInstallLock) {
+ succeeded = clearApplicationUserDataLIF(packageName, userId);
+ }
+ mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId);
+ synchronized (mLock) {
+ if (succeeded) {
+ resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
+ }
+ }
+ }
+ if (succeeded) {
+ // invoke DeviceStorageMonitor's update method to clear any notifications
+ DeviceStorageMonitorInternal dsm = LocalServices
+ .getService(DeviceStorageMonitorInternal.class);
+ if (dsm != null) {
+ dsm.checkMemory();
+ }
+ if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
+ == PERMISSION_GRANTED) {
+ unsuspendForSuspendingPackage(snapshotComputer(), packageName, userId);
+ removeAllDistractingPackageRestrictions(userId);
+ flushPackageRestrictionsAsUserInternalLocked(userId);
+ }
+ }
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(packageName, succeeded);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ } //end if observer
+ } //end run
+ });
+ }
+
+ @Override
+ public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ final int callingUid = Binder.getCallingUid();
+ enforceOwnerRights(ownerPackage, callingUid);
+ PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+ synchronized (mLock) {
+ CrossProfileIntentResolver resolver =
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
+ ArraySet<CrossProfileIntentFilter> set =
+ new ArraySet<>(resolver.filterSet());
+ for (CrossProfileIntentFilter filter : set) {
+ if (filter.getOwnerPackage().equals(ownerPackage)) {
+ resolver.removeFilter(filter);
+ }
+ }
+ }
+ scheduleWritePackageRestrictions(sourceUserId);
+ }
+
+ @Override
+ public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
+ mPreferredActivityHelper.clearPackagePersistentPreferredActivities(packageName, userId);
+ }
+
+ @Override
+ public void clearPackagePreferredActivities(String packageName) {
+ mPreferredActivityHelper.clearPackagePreferredActivities(packageName);
+ }
+
+ @Override
+ public String[] currentToCanonicalPackageNames(String[] names) {
+ return mComputer.currentToCanonicalPackageNames(names);
+ }
+
+ @Override
+ public void deleteApplicationCacheFiles(final String packageName,
+ final IPackageDataObserver observer) {
+ final int userId = UserHandle.getCallingUserId();
+ deleteApplicationCacheFilesAsUser(packageName, userId, observer);
+ }
+
+ @Override
+ public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
+ final IPackageDataObserver observer) {
+ final int callingUid = Binder.getCallingUid();
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES)
+ != PackageManager.PERMISSION_GRANTED) {
+ // If the caller has the old delete cache permission, silently ignore. Else throw.
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DELETE_CACHE_FILES)
+ == PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Calling uid " + callingUid + " does not have " +
+ android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES +
+ ", silently ignoring");
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
+ }
+ enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true,
+ /* checkShell= */ false, "delete application cache files");
+ final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_INSTANT_APPS);
+
+ final AndroidPackage pkg = getPackage(packageName);
+
+ // Queue up an async operation since the package deletion may take a little while.
+ mHandler.post(() -> {
+ final PackageStateInternal ps =
+ pkg == null ? null : getPackageStateInternal(pkg.getPackageName());
+ boolean doClearData = true;
+ if (ps != null) {
+ final boolean targetIsInstantApp =
+ ps.getUserStateOrDefault(UserHandle.getUserId(callingUid)).isInstantApp();
+ doClearData = !targetIsInstantApp
+ || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ }
+ if (doClearData) {
+ synchronized (mInstallLock) {
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
+ // We're only clearing cache files, so we don't care if the
+ // app is unfrozen and still able to run
+ mAppDataHelper.clearAppDataLIF(pkg, userId,
+ flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+ mAppDataHelper.clearAppDataLIF(pkg, userId,
+ flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ }
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(packageName, true);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
+ });
+ }
+
+ @Override
+ public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
+ final IPackageDeleteObserver2 observer, final int userId) {
+ PackageManagerService.this.deleteExistingPackageAsUser(versionedPackage, observer,
+ userId);
+ }
+
+ @Override
+ public void deletePackageAsUser(String packageName, int versionCode,
+ IPackageDeleteObserver observer, int userId, int flags) {
+ deletePackageVersioned(new VersionedPackage(packageName, versionCode),
+ new PackageManager.LegacyPackageDeleteObserver(observer).getBinder(), userId, flags);
+ }
+
+ @Override
+ public void deletePackageVersioned(VersionedPackage versionedPackage,
+ final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
+ PackageManagerService.this.deletePackageVersioned(versionedPackage, observer,
+ userId, deleteFlags);
+ }
+
+ @Override
+ public void deletePreloadsFileCache() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
+ "deletePreloadsFileCache");
+ File dir = Environment.getDataPreloadsFileCacheDirectory();
+ Slog.i(PackageManagerService.TAG, "Deleting preloaded file cache " + dir);
+ FileUtils.deleteContents(dir);
+ }
+
+ @Override
+ public void dumpProfiles(String packageName) {
+ /* Only the shell, root, or the app user should be able to dump profiles. */
+ final int callingUid = Binder.getCallingUid();
+ final String[] callerPackageNames = getPackagesForUid(callingUid);
+ if (callingUid != Process.SHELL_UID
+ && callingUid != Process.ROOT_UID
+ && !ArrayUtils.contains(callerPackageNames, packageName)) {
+ throw new SecurityException("dumpProfiles");
+ }
+
+ AndroidPackage pkg = mComputer.getPackage(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+
+ synchronized (mInstallLock) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
+ mArtManagerService.dumpProfiles(pkg);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ @Override
+ public void enterSafeMode() {
+ PackageManagerServiceUtils.enforceSystemOrRoot(
+ "Only the system can request entering safe mode");
+
+ if (!mSystemReady) {
+ mSafeMode = true;
+ }
+ }
+
+ @Override
+ public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ long millisecondsToDelay) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can extend verification timeouts");
+ final int callingUid = Binder.getCallingUid();
+
+ mHandler.post(() -> {
+ final PackageVerificationState state = mPendingVerification.get(id);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, callingUid);
+
+ long delay = millisecondsToDelay;
+ if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
+ delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
+ }
+ if (delay < 0) {
+ delay = 0;
+ }
+
+ if ((state != null) && !state.timeoutExtended()) {
+ state.extendTimeout();
+
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, delay);
+ }
+ });
+ }
+
+ @Override
+ public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
+ return mPreferredActivityHelper.findPersistentPreferredActivity(intent, userId);
+ }
+
+ @Override
+ public void finishPackageInstall(int token, boolean didLaunch) {
+ PackageManagerServiceUtils.enforceSystemOrRoot(
+ "Only the system is allowed to finish installs");
+
+ if (PackageManagerService.DEBUG_INSTALL) {
+ Slog.v(PackageManagerService.TAG, "BM finishing package install for " + token);
+ }
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+
+ final Message msg = mHandler.obtainMessage(PackageManagerService.POST_INSTALL, token, didLaunch ? 1 : 0);
+ mHandler.sendMessage(msg);
+ }
+
+ @WorkerThread
+ @Override
+ public void flushPackageRestrictionsAsUser(int userId) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return;
+ }
+ if (!mUserManager.exists(userId)) {
+ return;
+ }
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+ false /* checkShell */, "flushPackageRestrictions");
+ synchronized (mLock) {
+ flushPackageRestrictionsAsUserInternalLocked(userId);
+ }
+ }
+
+ @Override
+ public void forceDexOpt(String packageName) {
+ mDexOptHelper.forceDexOpt(packageName);
+ }
+
+
+ @Override
+ public void freeStorage(final String volumeUuid, final long freeStorageSize,
+ final @StorageManager.AllocateFlags int flags, final IntentSender pi) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CLEAR_APP_CACHE, TAG);
+ mHandler.post(() -> {
+ boolean success = false;
+ try {
+ PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags);
+ success = true;
+ } catch (IOException e) {
+ Slog.w(TAG, e);
+ }
+ if (pi != null) {
+ try {
+ pi.sendIntent(null, success ? 1 : 0, null, null, null);
+ } catch (SendIntentException e) {
+ Slog.w(TAG, e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
+ final @StorageManager.AllocateFlags int flags, final IPackageDataObserver observer) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CLEAR_APP_CACHE, null);
+ mHandler.post(() -> {
+ boolean success = false;
+ try {
+ PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags);
+ success = true;
+ } catch (IOException e) {
+ Slog.w(PackageManagerService.TAG, e);
+ }
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(null, success);
+ } catch (RemoteException e) {
+ Slog.w(PackageManagerService.TAG, e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
+ return mComputer.getActivityInfo(component, flags, userId);
+ }
+
+ @NonNull
+ @Override
+ public ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName) {
+ return mComputer.getAllIntentFilters(packageName);
+ }
+
+ @Override
+ public List<String> getAllPackages() {
+ return mComputer.getAllPackages();
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @NonNull
+ @Override
+ public String[] getAppOpPermissionPackages(@NonNull String permissionName) {
+ return mComputer.getAppOpPermissionPackages(permissionName);
+ }
+
+ @Override
+ public String getAppPredictionServicePackageName() {
+ return ensureSystemPackageName(
+ getPackageFromComponentString(R.string.config_defaultAppPredictionService));
+ }
+
+ @PackageManager.EnabledState
+ @Override
+ public int getApplicationEnabledSetting(@NonNull String packageName, @UserIdInt int userId) {
+ return mComputer.getApplicationEnabledSetting(packageName, userId);
+ }
+
+ /**
+ * Returns true if application is not found or there was an error. Otherwise it returns
+ * the hidden state of the package for the given user.
+ */
+ @Override
+ public boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
+ @UserIdInt int userId) {
+ return mComputer.getApplicationHiddenSettingAsUser(packageName, userId);
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName,
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
+ return mComputer.getApplicationInfo(packageName, flags, userId);
+ }
+
+ @Override
+ public IArtManager getArtManager() {
+ return mArtManagerService;
+ }
+
+ @Override
+ public @Nullable String getAttentionServicePackageName() {
+ return ensureSystemPackageName(
+ getPackageFromComponentString(R.string.config_defaultAttentionService));
+ }
+
+ @Override
+ public boolean getBlockUninstallForUser(@NonNull String packageName, @UserIdInt int userId) {
+ return mComputer.getBlockUninstallForUser(packageName, userId);
+ }
+
+ @Override
+ public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
+ final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages(
+ sequenceNumber, userId);
+
+ if (changedPackages != null) {
+ final List<String> packageNames = changedPackages.getPackageNames();
+ for (int index = packageNames.size() - 1; index >= 0; index--) {
+ // Filter out the changes if the calling package should not be able to see it.
+ final PackageSetting ps = mSettings.getPackageLPr(packageNames.get(index));
+ if (shouldFilterApplication(ps, callingUid, userId)) {
+ packageNames.remove(index);
+ }
+ }
+ }
+
+ return changedPackages;
+ }
+
+ @Override
+ public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
+ return mComputer.getComponentEnabledSetting(component, Binder.getCallingUid(), userId);
+ }
+
+ @Override
+ public String getContentCaptureServicePackageName() {
+ return ensureSystemPackageName(
+ getPackageFromComponentString(R.string.config_defaultContentCaptureService));
+ }
+
+ @Nullable
+ @Override
+ public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
+ @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
+ @NonNull int userId) {
+ return mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
+ }
+
+ /**
+ * Non-Binder method, support for the backup/restore mechanism: write the
+ * default browser (etc) settings in its canonical XML format. Returns the default
+ * browser XML representation as a byte array, or null if there is none.
+ */
+ @Override
+ public byte[] getDefaultAppsBackup(int userId) {
+ return mPreferredActivityHelper.getDefaultAppsBackup(userId);
+ }
+
+ @Override
+ public String getDefaultTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_servicesExtensionPackage));
+ }
+
+ @Override
+ public byte[] getDomainVerificationBackup(int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call getDomainVerificationBackup()");
+ }
+
+ try {
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ TypedXmlSerializer serializer = Xml.resolveSerializer(output);
+ mDomainVerificationManager.writeSettings(snapshotComputer(), serializer, true,
+ userId);
+ return output.toByteArray();
+ }
+ } catch (Exception e) {
+ if (PackageManagerService.DEBUG_BACKUP) {
+ Slog.e(PackageManagerService.TAG, "Unable to write domain verification for backup", e);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public int getFlagsForUid(int uid) {
+ return mComputer.getFlagsForUid(uid);
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName, @UserIdInt int userId) {
+ return mComputer.getHarmfulAppWarning(packageName, userId);
+ }
+
+ @Override
+ public IBinder getHoldLockToken() {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("getHoldLockToken requires a debuggable build");
+ }
+
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS,
+ "getHoldLockToken requires INJECT_EVENTS permission");
+
+ final Binder token = new Binder();
+ token.attachInterface(this, "holdLock:" + Binder.getCallingUid());
+ return token;
+ }
+
+ @Override
+ public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
+ }
+
+ public String getIncidentReportApproverPackageName() {
+ return ensureSystemPackageName(mContext.getString(
+ R.string.config_incidentReportApproverPackage));
+ }
+
+ @Override
+ public int getInstallLocation() {
+ // allow instant app access
+ return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
+ InstallLocationUtils.APP_INSTALL_AUTO);
+ }
+
+ @PackageManager.InstallReason
+ @Override
+ public int getInstallReason(@NonNull String packageName, @UserIdInt int userId) {
+ return mComputer.getInstallReason(packageName, userId);
+ }
+
+ @Override
+ @Nullable
+ public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) {
+ return mComputer.getInstallSourceInfo(packageName);
+ }
+
+ @Override
+ public ParceledListSlice<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ return new ParceledListSlice<>(
+ mComputer.getInstalledApplications(flags, userId, callingUid));
+ }
+
+ @Override
+ public List<ModuleInfo> getInstalledModules(int flags) {
+ return mModuleInfoProvider.getInstalledModules(flags);
+ }
+
+ @Override
+ public ParceledListSlice<PackageInfo> getInstalledPackages(
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ return mComputer.getInstalledPackages(flags, userId);
+ }
+
+ @Nullable
+ @Override
+ public String getInstallerPackageName(@NonNull String packageName) {
+ return mComputer.getInstallerPackageName(packageName);
+ }
+
+ @Override
+ public String getInstantAppAndroidId(String packageName, int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
+ "getInstantAppAndroidId");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getInstantAppAndroidId");
+ // Make sure the target is an Instant App.
+ if (!isInstantApp(packageName, userId)) {
+ return null;
+ }
+ return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId);
+ }
+
+ @Override
+ public byte[] getInstantAppCookie(String packageName, int userId) {
+ if (HIDE_EPHEMERAL_APIS) {
+ return null;
+ }
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getInstantAppCookie");
+ if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
+ return null;
+ }
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null || packageState.getPkg() == null) {
+ return null;
+ }
+ return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId);
+ }
+
+ @Override
+ public Bitmap getInstantAppIcon(String packageName, int userId) {
+ if (HIDE_EPHEMERAL_APIS) {
+ return null;
+ }
+
+ if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+ "getInstantAppIcon");
+ }
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getInstantAppIcon");
+
+ return mInstantAppRegistry.getInstantAppIcon(packageName, userId);
+ }
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ return mInstantAppInstallerActivity == null
+ ? null : mInstantAppInstallerActivity.getComponentName();
+ }
+
+ @Override
+ public @Nullable ComponentName getInstantAppResolverComponent() {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ return getInstantAppResolver();
+ }
+
+ @Override
+ public ComponentName getInstantAppResolverSettingsComponent() {
+ return mInstantAppResolverSettingsComponent;
+ }
+
+ @Override
+ public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
+ if (PackageManagerService.HIDE_EPHEMERAL_APIS) {
+ return null;
+ }
+ if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
+ "getEphemeralApplications");
+ }
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getEphemeralApplications");
+
+ Computer computer = snapshotComputer();
+ List<InstantAppInfo> instantApps = mInstantAppRegistry.getInstantApps(computer, userId);
+ if (instantApps != null) {
+ return new ParceledListSlice<>(instantApps);
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName component, int flags) {
+ return mComputer.getInstrumentationInfo(component, flags);
+ }
+
+ @Deprecated
+ @Override
+ public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
+ String packageName) {
+ return ParceledListSlice.emptyList();
+ }
+
+ @Deprecated
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) {
+ return mDomainVerificationManager.getLegacyState(packageName, userId);
+ }
+
+ @Nullable
+ @Override
+ public KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias) {
+ return mComputer.getKeySetByAlias(packageName, alias);
+ }
+
+ @Override
+ public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
+ return mPreferredActivityHelper.getLastChosenActivity(intent, resolvedType, flags);
+ }
+
+ @Override
+ public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId) throws RemoteException {
+ return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
+ packageName, callingPackage, featureId, userId);
+ }
+
+ @Override
+ public List<String> getMimeGroup(String packageName, String mimeGroup) {
+ enforceOwnerRights(packageName, Binder.getCallingUid());
+ return getMimeGroupInternal(packageName, mimeGroup);
+ }
+
+ @Override
+ public ModuleInfo getModuleInfo(String packageName, @PackageManager.ModuleInfoFlags int flags) {
+ return PackageManagerService.this.getModuleInfo(packageName, flags);
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ return mMoveCallbacks.mLastStatus.get(moveId);
+ }
+
+ @Nullable
+ @Override
+ public String getNameForUid(int uid) {
+ return mComputer.getNameForUid(uid);
+ }
+
+ @Nullable
+ @Override
+ public String[] getNamesForUids(@NonNull int[] uids) {
+ return mComputer.getNamesForUids(uids);
+ }
+
+ @Override
+ public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlagsBits long flags,
+ int userId) {
+ return mComputer.getPackageGids(packageName, flags, userId);
+ }
+
+ @Override
+ public PackageInfo getPackageInfo(String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ return mComputer.getPackageInfo(packageName, flags, userId);
+ }
+
+ @Override
+ public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ return mComputer.getPackageInfoInternal(versionedPackage.getPackageName(),
+ versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
+ }
+
+ @Override
+ public IPackageInstaller getPackageInstaller() {
+ // Return installer service for internal calls.
+ if (PackageManagerServiceUtils.isSystemOrRoot()) {
+ return mInstallerService;
+ }
+ // Return null for InstantApps.
+ if (snapshotComputer().getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ return mInstallerService;
+ }
+
+ @Override
+ public void getPackageSizeInfo(final String packageName, int userId,
+ final IPackageStatsObserver observer) {
+ throw new UnsupportedOperationException(
+ "Shame on you for calling the hidden API getPackageSizeInfo(). Shame!");
+ }
+
+ @Override
+ public int getPackageUid(@NonNull String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
+ return mComputer.getPackageUid(packageName, flags, userId);
+ }
+
+ /**
+ * <em>IMPORTANT:</em> Not all packages returned by this method may be known
+ * to the system. There are two conditions in which this may occur:
+ * <ol>
+ * <li>The package is on adoptable storage and the device has been removed</li>
+ * <li>The package is being removed and the internal structures are partially updated</li>
+ * </ol>
+ * The second is an artifact of the current data structures and should be fixed. See
+ * b/111075456 for one such instance.
+ * This binder API is cached. If the algorithm in this method changes,
+ * or if the underlying objecs (as returned by getSettingLPr()) change
+ * then the logic that invalidates the cache must be revisited. See
+ * calls to invalidateGetPackagesForUidCache() to locate the points at
+ * which the cache is invalidated.
+ */
+ @Override
+ public String[] getPackagesForUid(int uid) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(uid);
+ mComputer.enforceCrossUserOrProfilePermission(callingUid, userId,
+ /* requireFullPermission */ false,
+ /* checkShell */ false, "getPackagesForUid");
+ return mComputer.getPackagesForUid(uid);
+ }
+
+ @Override
+ public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
+ @UserIdInt int userId) {
+ return mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
+ }
+
+ @Override
+ public String getPermissionControllerPackageName() {
+ final int callingUid = Binder.getCallingUid();
+ if (mComputer.getPackageStateFiltered(mRequiredPermissionControllerPackage,
+ callingUid, UserHandle.getUserId(callingUid)) != null) {
+ return mRequiredPermissionControllerPackage;
+ }
+
+ throw new IllegalStateException("PermissionController is not found");
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
+ return PackageManagerService.this.getPermissionGroupInfo(groupName, flags);
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return ParceledListSlice.emptyList();
+ }
+ return new ParceledListSlice<>(mComputer.getPersistentApplications(mSafeMode, flags));
+ }
+
+ @Override
+ public int getPreferredActivities(List<IntentFilter> outFilters,
+ List<ComponentName> outActivities, String packageName) {
+ return mPreferredActivityHelper.getPreferredActivities(outFilters, outActivities,
+ packageName, snapshotComputer());
+ }
+
+ /**
+ * Non-Binder method, support for the backup/restore mechanism: write the
+ * full set of preferred activities in its canonical XML format. Returns the
+ * XML output as a byte array, or null if there is none.
+ */
+ @Override
+ public byte[] getPreferredActivityBackup(int userId) {
+ return mPreferredActivityHelper.getPreferredActivityBackup(userId);
+ }
+
+ @Override
+ public int getPrivateFlagsForUid(int uid) {
+ return mComputer.getPrivateFlagsForUid(uid);
+ }
+
+ @Override
+ public PackageManager.Property getProperty(String propertyName, String packageName, String className) {
+ Objects.requireNonNull(propertyName);
+ Objects.requireNonNull(packageName);
+ PackageStateInternal packageState = mComputer.getPackageStateFiltered(packageName,
+ Binder.getCallingUid(), UserHandle.getCallingUserId());
+ if (packageState == null) {
+ return null;
+ }
+ return mPackageProperty.getProperty(propertyName, packageName, className);
+ }
+
+ @Nullable
+ @Override
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
+ return mComputer.getProviderInfo(component, flags, userId);
+ }
+
+ @Override
+ public ActivityInfo getReceiverInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
+ return mComputer.getReceiverInfo(component, flags, userId);
+ }
+
+ @Override
+ public @Nullable String getRotationResolverPackageName() {
+ return ensureSystemPackageName(
+ getPackageFromComponentString(R.string.config_defaultRotationResolverService));
+ }
+
+ @Override
+ public int getRuntimePermissionsVersion(@UserIdInt int userId) {
+ Preconditions.checkArgumentNonnegative(userId);
+ enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
+ "getRuntimePermissionVersion");
+ return mSettings.getDefaultRuntimePermissionsVersion(userId);
+ }
+
+ @Nullable
+ @Override
+ public ServiceInfo getServiceInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
+ return mComputer.getServiceInfo(component, flags, userId);
+ }
+
+ @Override
+ public @NonNull String getServicesSystemSharedLibraryPackageName() {
+ return mServicesExtensionPackageName;
+ }
+
+ @Override
+ public String getSetupWizardPackageName() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Non-system caller");
+ }
+ return mPmInternal.getSetupWizardPackageName();
+ }
+
+ @Override
+ public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ return mComputer.getSharedLibraries(packageName, flags, userId);
+ }
+
+ @Override
+ public @NonNull String getSharedSystemSharedLibraryPackageName() {
+ return mSharedSystemSharedLibraryPackageName;
+ }
+
+ @Nullable
+ @Override
+ public KeySet getSigningKeySet(@NonNull String packageName) {
+ return mComputer.getSigningKeySet(packageName);
+ }
+
+ @Override
+ public String getSplashScreenTheme(@NonNull String packageName, int userId) {
+ PackageStateInternal packageState =
+ getPackageStateInstalledFiltered(packageName, Binder.getCallingUid(), userId);
+ return packageState == null ? null
+ : packageState.getUserStateOrDefault(userId).getSplashScreenTheme();
+ }
+
+ @Override
+ public String getSdkSandboxPackageName() {
+ return mRequiredSdkSandboxPackage;
+ }
+
+ @Override
+ public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (getPackageUid(packageName, 0, userId) != callingUid) {
+ throw new SecurityException("Calling package " + packageName
+ + " does not belong to calling uid " + callingUid);
+ }
+ return mSuspendPackageHelper.getSuspendedPackageAppExtras(
+ packageName, userId, callingUid);
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
+ // allow instant applications
+ ArrayList<FeatureInfo> res;
+ synchronized (mAvailableFeatures) {
+ res = new ArrayList<>(mAvailableFeatures.size() + 1);
+ res.addAll(mAvailableFeatures.values());
+ }
+ final FeatureInfo fi = new FeatureInfo();
+ fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ res.add(fi);
+
+ return new ParceledListSlice<>(res);
+ }
+
+ @Override
+ public String getSystemCaptionsServicePackageName() {
+ return ensureSystemPackageName(
+ getPackageFromComponentString(R.string.config_defaultSystemCaptionsService));
+ }
+
+ @Nullable
+ @Override
+ public String[] getSystemSharedLibraryNames() {
+ return mComputer.getSystemSharedLibraryNames();
+ }
+
+ @Override
+ public String getSystemTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_defaultTextClassifierPackage));
+ }
+
+ @Override
+ public int getTargetSdkVersion(@NonNull String packageName) {
+ return mComputer.getTargetSdkVersion(packageName);
+ }
+
+ @Override
+ public int getUidForSharedUser(@NonNull String sharedUserName) {
+ return mComputer.getUidForSharedUser(sharedUserName);
+ }
+
+ @Override
+ public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
+ Objects.requireNonNull(packageNames, "packageNames cannot be null");
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "getUnsuspendablePackagesForUser");
+ final int callingUid = Binder.getCallingUid();
+ if (UserHandle.getUserId(callingUid) != userId) {
+ throw new SecurityException("Calling uid " + callingUid
+ + " cannot query getUnsuspendablePackagesForUser for user " + userId);
+ }
+ return mSuspendPackageHelper.getUnsuspendablePackagesForUser(snapshotComputer(),
+ packageNames, userId, callingUid);
+ }
+
+ @Override
+ public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can read the verifier device identity");
+
+ synchronized (mLock) {
+ return mSettings.getVerifierDeviceIdentityLPw(mLiveComputer);
+ }
+ }
+
+ @Override
+ public String getWellbeingPackageName() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return CollectionUtils.firstOrNull(
+ mContext.getSystemService(RoleManager.class).getRoleHolders(
+ RoleManager.ROLE_SYSTEM_WELLBEING));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void grantImplicitAccess(int recipientUid, @NonNull String visibleAuthority) {
+ final Computer snapshot = snapshotComputer();
+ final int recipientUserId = UserHandle.getUserId(recipientUid);
+ final ProviderInfo providerInfo =
+ snapshot.getGrantImplicitAccessProviderInfo(recipientUid, visibleAuthority);
+ if (providerInfo == null) {
+ return;
+ }
+ int visibleUid = providerInfo.applicationInfo.uid;
+ PackageManagerService.this.grantImplicitAccess(snapshot, recipientUserId,
+ null /*Intent*/, UserHandle.getAppId(recipientUid), visibleUid,
+ false /*direct*/, false /* retainOnUpdate */);
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public void grantRuntimePermission(String packageName, String permName, final int userId) {
+ // Because this is accessed via the package manager service AIDL,
+ // go through the permission manager service AIDL
+ mContext.getSystemService(PermissionManager.class)
+ .grantRuntimePermission(packageName, permName, UserHandle.of(userId));
+ }
+
+ @Override
+ public boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate,
+ @PackageManager.CertificateInputType int type) {
+ return mComputer.hasSigningCertificate(packageName, certificate, type);
+ }
+
+ @Override
+ public boolean hasSystemFeature(String name, int version) {
+ return PackageManagerService.this.hasSystemFeature(name, version);
+ }
+
+ @Override
+ public boolean hasSystemUidErrors() {
+ // allow instant applications
+ return false;
+ }
+
+ @Override
+ public boolean hasUidSigningCertificate(int uid, @NonNull byte[] certificate,
+ @PackageManager.CertificateInputType int type) {
+ return mComputer.hasUidSigningCertificate(uid, certificate, type);
+ }
+
+ @Override
+ public void holdLock(IBinder token, int durationMs) {
+ mTestUtilityService.verifyHoldLockToken(token);
+
+ synchronized (mLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+ int installReason, List<String> whiteListedPermissions) {
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ installReason, whiteListedPermissions, null);
+ }
+
+ @Override
+ public boolean isAutoRevokeWhitelisted(String packageName) {
+ int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ Binder.getCallingUid(), packageName);
+ return mode == MODE_IGNORED;
+ }
+
+ @Override
+ public boolean isDeviceUpgrading() {
+ return PackageManagerService.this.isDeviceUpgrading();
+ }
+
+ @Override
+ public boolean isFirstBoot() {
+ return PackageManagerService.this.isFirstBoot();
+ }
+
+ @Override
+ public boolean isInstantApp(String packageName, int userId) {
+ return mComputer.isInstantApp(packageName, userId);
+ }
+
+ @Override
+ public boolean isOnlyCoreApps() {
+ return PackageManagerService.this.isOnlyCoreApps();
+ }
+
+ @Override
+ public boolean isPackageAvailable(String packageName, int userId) {
+ return mComputer.isPackageAvailable(packageName, userId);
+ }
+
+ @Override
+ public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
+ return PackageManagerService.this.isPackageDeviceAdminOnAnyUser(packageName);
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySet(@NonNull String packageName, @NonNull KeySet ks) {
+ return mComputer.isPackageSignedByKeySet(packageName, ks);
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySetExactly(@NonNull String packageName, @NonNull KeySet ks) {
+ return mComputer.isPackageSignedByKeySetExactly(packageName, ks);
+ }
+
+ @Override
+ public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ true /*checkShell*/, "isPackageStateProtected");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
+ && checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + MANAGE_DEVICE_ADMINS + " permission.");
+ }
+
+ return mProtectedPackages.isPackageStateProtected(userId, packageName);
+ }
+
+ @Override
+ public boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) {
+ return mComputer.isPackageSuspendedForUser(packageName, userId);
+ }
+
+ @Override
+ public boolean isProtectedBroadcast(String actionName) {
+ if (actionName != null) {
+ // TODO: remove these terrible hacks
+ if (actionName.startsWith("android.net.netmon.lingerExpired")
+ || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
+ || actionName.startsWith("com.android.internal.telephony.data-reconnect")
+ || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
+ return true;
+ }
+ }
+ // allow instant applications
+ synchronized (mProtectedBroadcasts) {
+ return mProtectedBroadcasts.contains(actionName);
+ }
+ }
+
+ @Override
+ public boolean isSafeMode() {
+ // allow instant applications
+ return mSafeMode;
+ }
+
+ @Override
+ public boolean isStorageLow() {
+ return PackageManagerService.this.isStorageLow();
+ }
+
+ @Override
+ public boolean isUidPrivileged(int uid) {
+ return mComputer.isUidPrivileged(uid);
+ }
+
+ /**
+ * Logs process start information (including base APK hash) to the security log.
+ * @hide
+ */
+ @Override
+ public void logAppProcessStartIfNeeded(String packageName, String processName, int uid,
+ String seinfo, String apkFile, int pid) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return;
+ }
+ if (!SecurityLog.isLoggingEnabled()) {
+ return;
+ }
+ mProcessLoggingHandler.logAppProcessStart(mContext, mPmInternal, apkFile, packageName,
+ processName, uid, seinfo, pid);
+ }
+
+ @Override
+ public int movePackage(final String packageName, final String volumeUuid) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.MOVE_PACKAGE, null);
+
+ final int callingUid = Binder.getCallingUid();
+ final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
+ final int moveId = mNextMoveId.getAndIncrement();
+ mHandler.post(() -> {
+ try {
+ MovePackageHelper movePackageHelper =
+ new MovePackageHelper(PackageManagerService.this);
+ movePackageHelper.movePackageInternal(
+ packageName, volumeUuid, moveId, callingUid, user);
+ } catch (PackageManagerException e) {
+ Slog.w(PackageManagerService.TAG, "Failed to move " + packageName, e);
+ mMoveCallbacks.notifyStatusChanged(moveId, e.error);
+ }
+ });
+ return moveId;
+ }
+
+ @Override
+ public int movePrimaryStorage(String volumeUuid) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.MOVE_PACKAGE, null);
+
+ final int realMoveId = mNextMoveId.getAndIncrement();
+ final Bundle extras = new Bundle();
+ extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid);
+ mMoveCallbacks.notifyCreated(realMoveId, extras);
+
+ final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() {
+ @Override
+ public void onCreated(int moveId, Bundle extras) {
+ // Ignored
+ }
+
+ @Override
+ public void onStatusChanged(int moveId, int status, long estMillis) {
+ mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis);
+ }
+ };
+
+ final StorageManager storage = mInjector.getSystemService(StorageManager.class);
+ storage.setPrimaryStorageUuid(volumeUuid, callback);
+ return realMoveId;
+ }
+
+ @Override
+ public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
+ String loaderIsa) {
+ int callingUid = Binder.getCallingUid();
+ if (PackageManagerService.PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && callingUid != Process.SYSTEM_UID) {
+ Slog.w(PackageManagerService.TAG, "Non System Server process reporting dex loads as system server. uid="
+ + callingUid);
+ // Do not record dex loads from processes pretending to be system server.
+ // Only the system server should be assigned the package "android", so reject calls
+ // that don't satisfy the constraint.
+ //
+ // notifyDexLoad is a PM API callable from the app process. So in theory, apps could
+ // craft calls to this API and pretend to be system server. Doing so poses no particular
+ // danger for dex load reporting or later dexopt, however it is a sensible check to do
+ // in order to verify the expectations.
+ return;
+ }
+
+ int userId = UserHandle.getCallingUserId();
+ ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
+ if (ai == null) {
+ Slog.w(PackageManagerService.TAG, "Loading a package that does not exist for the calling user. package="
+ + loadingPackageName + ", user=" + userId);
+ return;
+ }
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId,
+ Process.isIsolated(callingUid));
+ }
+
+ @Override
+ public void notifyPackageUse(String packageName, int reason) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final boolean notify;
+ if (getInstantAppPackageName(callingUid) != null) {
+ notify = isCallerSameApp(packageName, callingUid);
+ } else {
+ notify = !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
+ }
+ if (!notify) {
+ return;
+ }
+
+ notifyPackageUseInternal(packageName, reason);
+ }
+
+ @Override
+ public void overrideLabelAndIcon(@NonNull ComponentName componentName,
+ @NonNull String nonLocalizedLabel, int icon, int userId) {
+ if (TextUtils.isEmpty(nonLocalizedLabel)) {
+ throw new IllegalArgumentException("Override label should be a valid String");
+ }
+ updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId);
+ }
+
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ @Override
+ public boolean performDexOptMode(String packageName,
+ boolean checkProfiles, String targetCompilerFilter, boolean force,
+ boolean bootComplete, String splitName) {
+ return mDexOptHelper.performDexOptMode(packageName, checkProfiles, targetCompilerFilter,
+ force, bootComplete, splitName);
+ }
+
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter on the
+ * secondary dex files belonging to the given package.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ @Override
+ public boolean performDexOptSecondary(String packageName, String compilerFilter,
+ boolean force) {
+ return mDexOptHelper.performDexOptSecondary(packageName, compilerFilter, force);
+ }
+
+ @NonNull
+ @Override
+ public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
+ int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+ @Nullable String metaDataKey) {
+ return mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
+ }
+
+ @NonNull
+ @Override
+ public ParceledListSlice<InstrumentationInfo> queryInstrumentation(
+ @NonNull String targetPackage, int flags) {
+ return mComputer.queryInstrumentation(targetPackage, flags);
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+
+ return new ParceledListSlice<>(snapshotComputer().queryIntentActivitiesInternal(intent,
+ resolvedType, flags, userId));
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+ Intent[] specifics, String[] specificTypes, Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
+ snapshotComputer(), caller, specifics, specificTypes, intent, resolvedType, flags,
+ userId));
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
+ snapshotComputer(), intent, resolvedType, flags, userId));
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
+ snapshotComputer(), intent, resolvedType, flags, userId, Binder.getCallingUid()));
+ }
+
+ @Override
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
+ String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ return new ParceledListSlice<>(snapshotComputer().queryIntentServicesInternal(
+ intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
+ }
+
+ @Override
+ public ParceledListSlice<PackageManager.Property> queryProperty(
+ String propertyName, @PackageManager.PropertyLocation int componentType) {
+ Objects.requireNonNull(propertyName);
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ final List<PackageManager.Property> result =
+ mPackageProperty.queryProperty(propertyName, componentType, packageName -> {
+ final PackageStateInternal ps = getPackageStateInternal(packageName);
+ return shouldFilterApplication(ps, callingUid, callingUserId);
+ });
+ if (result == null) {
+ return ParceledListSlice.emptyList();
+ }
+ return new ParceledListSlice<>(result);
+ }
+
+ @Deprecated
+ public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
+ mComputer.querySyncProviders(mSafeMode, outNames, outInfo);
+ }
+
+ /**
+ * Reconcile the information we have about the secondary dex files belonging to
+ * {@code packageName} and the actual dex files. For all dex files that were
+ * deleted, update the internal records and delete the generated oat files.
+ */
+ @Override
+ public void reconcileSecondaryDexFiles(String packageName) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return;
+ } else if (isInstantAppInternal(
+ packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
+ return;
+ }
+ mDexManager.reconcileSecondaryDexFiles(packageName);
+ }
+
+ @Override
+ public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
+ IDexModuleRegisterCallback callback) {
+ int userId = UserHandle.getCallingUserId();
+ ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId);
+ DexManager.RegisterDexModuleResult result;
+ if (ai == null) {
+ Slog.w(PackageManagerService.TAG, "Registering a dex module for a package that does not exist for the" +
+ " calling user. package=" + packageName + ", user=" + userId);
+ result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
+ } else {
+ result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
+ }
+
+ if (callback != null) {
+ mHandler.post(() -> {
+ try {
+ callback.onDexModuleRegistered(dexModulePath, result.success, result.message);
+ } catch (RemoteException e) {
+ Slog.w(PackageManagerService.TAG, "Failed to callback after module registration " + dexModulePath, e);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void registerMoveCallback(IPackageMoveObserver callback) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ mMoveCallbacks.register(callback);
+ }
+
+ // NOTE: Can't remove due to unsupported app usage
+ @Override
+ public void removePermission(String permName) {
+ // Because this is accessed via the package manager service AIDL,
+ // go through the permission manager service AIDL
+ mContext.getSystemService(PermissionManager.class).removePermission(permName);
+ }
+
+ @Override
+ public void replacePreferredActivity(IntentFilter filter, int match,
+ ComponentName[] set, ComponentName activity, int userId) {
+ mPreferredActivityHelper.replacePreferredActivity(new WatchedIntentFilter(filter),
+ match, set, activity, userId);
+ }
+
+ @Override
+ public void resetApplicationPreferences(int userId) {
+ mPreferredActivityHelper.resetApplicationPreferences(userId);
+ }
+
+ @Override
+ public ProviderInfo resolveContentProvider(String name,
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ return mComputer.resolveContentProvider(name, flags, userId, Binder.getCallingUid());
+ }
+
+ @Override
+ public ResolveInfo resolveIntent(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(), intent, resolvedType,
+ flags, 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent, resolvedType,
+ flags, userId, callingUid);
+ }
+
+ @Override
+ public void restoreDefaultApps(byte[] backup, int userId) {
+ mPreferredActivityHelper.restoreDefaultApps(backup, userId);
+ }
+
+ @Override
+ public void restoreDomainVerification(byte[] backup, int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call restorePreferredActivities()");
+ }
+
+ try {
+ ByteArrayInputStream input = new ByteArrayInputStream(backup);
+ TypedXmlPullParser parser = Xml.resolvePullParser(input);
+
+ // User ID input isn't necessary here as it assumes the user integers match and that
+ // the only states inside the backup XML are for the target user.
+ mDomainVerificationManager.restoreSettings(snapshotComputer(), parser);
+ input.close();
+ } catch (Exception e) {
+ if (PackageManagerService.DEBUG_BACKUP) {
+ Slog.e(PackageManagerService.TAG, "Exception restoring domain verification: " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) {
+ updateComponentLabelIcon(componentName, null, null, userId);
+ }
+
+ @Override
+ public void restorePreferredActivities(byte[] backup, int userId) {
+ mPreferredActivityHelper.restorePreferredActivities(backup, userId);
+ }
+
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
+ "sendDeviceCustomizationReadyBroadcast");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ BroadcastHelper.sendDeviceCustomizationReadyBroadcast();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void setApplicationCategoryHint(String packageName, int categoryHint,
+ String callerPackageName) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ throw new SecurityException("Instant applications don't have access to this method");
+ }
+ mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
+ callerPackageName);
+
+ final PackageStateMutator.InitialState initialState = recordInitialState();
+
+ final FunctionalUtils.ThrowingFunction<Computer, PackageStateMutator.Result>
+ implementation = computer -> {
+ PackageStateInternal packageState = computer.getPackageStateFiltered(packageName,
+ Binder.getCallingUid(), UserHandle.getCallingUserId());
+ if (packageState == null) {
+ throw new IllegalArgumentException("Unknown target package " + packageName);
+ }
+
+ if (!Objects.equals(callerPackageName,
+ packageState.getInstallSource().installerPackageName)) {
+ throw new IllegalArgumentException("Calling package " + callerPackageName
+ + " is not installer for " + packageName);
+ }
+
+ if (packageState.getCategoryOverride() != categoryHint) {
+ return commitPackageStateMutation(initialState,
+ packageName, state -> state.setCategoryOverride(categoryHint));
+ } else {
+ return null;
+ }
+ };
+
+ PackageStateMutator.Result result = implementation.apply(snapshotComputer());
+ if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) {
+ // TODO: Specific return value of what state changed?
+ // The installer on record might have changed, retry with lock
+ synchronized (mPackageStateWriteLock) {
+ result = implementation.apply(snapshotComputer());
+ }
+ }
+
+ if (result != null && result.isCommitted()) {
+ scheduleWriteSettings();
+ }
+ }
+
+ @Override
+ public void setApplicationEnabledSetting(String appPackageName,
+ int newState, int flags, int userId, String callingPackage) {
+ if (!mUserManager.exists(userId)) return;
+ if (callingPackage == null) {
+ callingPackage = Integer.toString(Binder.getCallingUid());
+ }
+
+ setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(appPackageName, newState, flags)),
+ userId, callingPackage);
+ }
+
+ @Override
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
+ int userId) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
+
+ if (hidden && isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
+ return false;
+ }
+
+ // Do not allow "android" is being disabled
+ if ("android".equals(packageName)) {
+ Slog.w(TAG, "Cannot hide package: android");
+ return false;
+ }
+
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ final PackageStateInternal packageState =
+ mComputer.getPackageStateFiltered(packageName, callingUid, userId);
+ if (packageState == null) {
+ return false;
+ }
+
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ AndroidPackage pkg = packageState.getPkg();
+ if (pkg != null) {
+ // Cannot hide SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ return false;
+ }
+ }
+ // Only allow protected packages to hide themselves.
+ if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId())
+ && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Not hiding protected package: " + packageName);
+ return false;
+ }
+
+ if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
+ return false;
+ }
+
+ commitPackageStateMutation(null, packageName, packageState1 ->
+ packageState1.userState(userId).setHidden(hidden));
+
+ final PackageStateInternal newPackageState = getPackageStateInternal(packageName);
+
+ if (hidden) {
+ killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg");
+ sendApplicationHiddenForUser(packageName, newPackageState, userId);
+ } else {
+ sendPackageAddedForUser(packageName, newPackageState, userId, DataLoaderType.NONE);
+ }
+
+ scheduleWritePackageRestrictions(userId);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
+ public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
+ int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.DELETE_PACKAGES, null);
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState != null && packageState.getPkg() != null) {
+ AndroidPackage pkg = packageState.getPkg();
+ // Cannot block uninstall SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
+ + " providing SDK library: " + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot block uninstall of static shared libs as they are
+ // considered a part of the using app (emulating static linking).
+ // Also static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
+ + " providing static shared library: " + pkg.getStaticSharedLibName());
+ return false;
+ }
+ }
+ synchronized (mLock) {
+ mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
+ }
+
+ scheduleWritePackageRestrictions(userId);
+ return true;
+ }
+
+ @Override
+ public void setComponentEnabledSetting(ComponentName componentName,
+ int newState, int flags, int userId) {
+ if (!mUserManager.exists(userId)) return;
+
+ setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(componentName, newState, flags)),
+ userId, null /* callingPackage */);
+ }
+
+ @Override
+ public void setComponentEnabledSettings(List<PackageManager.ComponentEnabledSetting> settings, int userId) {
+ if (!mUserManager.exists(userId)) return;
+ if (settings == null || settings.isEmpty()) {
+ throw new IllegalArgumentException("The list of enabled settings is empty");
+ }
+
+ setEnabledSettings(settings, userId, null /* callingPackage */);
+ }
+
+ @Override
+ public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+ int restrictionFlags, int userId) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "setDistractingPackageRestrictionsAsUser");
+
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
+ && UserHandle.getUserId(callingUid) != userId) {
+ throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
+ + userId);
+ }
+ Objects.requireNonNull(packageNames, "packageNames cannot be null");
+ if (restrictionFlags != 0
+ && !mSuspendPackageHelper.isSuspendAllowedForUser(userId, callingUid)) {
+ Slog.w(PackageManagerService.TAG, "Cannot restrict packages due to restrictions on user " + userId);
+ return packageNames;
+ }
+
+ final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray changedUids = new IntArray(packageNames.length);
+ final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+
+ ArraySet<String> changesToCommit = new ArraySet<>();
+ Computer computer = snapshotComputer();
+ final boolean[] canRestrict = (restrictionFlags != 0)
+ ? mSuspendPackageHelper.canSuspendPackageForUser(computer, packageNames, userId,
+ callingUid) : null;
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ final PackageStateInternal packageState =
+ computer.getPackageStateInternal(packageName);
+ if (packageState == null
+ || computer.shouldFilterApplication(packageState, callingUid, userId)) {
+ Slog.w(PackageManagerService.TAG, "Could not find package setting for package: " + packageName
+ + ". Skipping...");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ if (canRestrict != null && !canRestrict[i]) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
+ .getDistractionFlags();
+ if (restrictionFlags != oldDistractionFlags) {
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ changesToCommit.add(packageName);
+ }
+ }
+
+ commitPackageStateMutation(null, mutator -> {
+ final int size = changesToCommit.size();
+ for (int index = 0; index < size; index++) {
+ mutator.forPackage(changesToCommit.valueAt(index))
+ .userState(userId)
+ .setDistractionFlags(restrictionFlags);
+ }
+ });
+
+ if (!changedPackagesList.isEmpty()) {
+ final String[] changedPackages = changedPackagesList.toArray(
+ new String[changedPackagesList.size()]);
+ mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
+ changedPackages, changedUids.toArray(), userId, restrictionFlags));
+ scheduleWritePackageRestrictions(userId);
+ }
+ return unactionedPackages.toArray(new String[0]);
+ }
+
+ @Override
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
+ true /*checkShell*/, "setHarmfulAppInfo");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
+ checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + SET_HARMFUL_APP_WARNINGS + " permission.");
+ }
+
+ PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+ packageState -> packageState.userState(userId)
+ .setHarmfulAppWarning(warning == null ? null : warning.toString()));
+ if (result.isSpecificPackageNull()) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ scheduleWritePackageRestrictions(userId);
+ }
+
+ @Override
+ public void setHomeActivity(ComponentName comp, int userId) {
+ mPreferredActivityHelper.setHomeActivity(comp, userId);
+ }
+
+ @Override
+ public boolean setInstallLocation(int loc) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS,
+ null);
+ if (getInstallLocation() == loc) {
+ return true;
+ }
+ if (loc == InstallLocationUtils.APP_INSTALL_AUTO
+ || loc == InstallLocationUtils.APP_INSTALL_INTERNAL
+ || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void setInstallerPackageName(String targetPackage, String installerPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final FunctionalUtils.ThrowingCheckedFunction<Computer, Boolean, RuntimeException>
+ implementation = computer -> {
+ if (computer.getInstantAppPackageName(callingUid) != null) {
+ return false;
+ }
+
+ PackageStateInternal targetPackageState =
+ computer.getPackageStateInternal(targetPackage);
+ if (targetPackageState == null
+ || computer.shouldFilterApplication(targetPackageState, callingUid,
+ callingUserId)) {
+ throw new IllegalArgumentException("Unknown target package: " + targetPackage);
+ }
+
+ PackageStateInternal installerPackageState = null;
+ if (installerPackageName != null) {
+ installerPackageState = computer.getPackageStateInternal(installerPackageName);
+ if (installerPackageState == null
+ || shouldFilterApplication(
+ installerPackageState, callingUid, callingUserId)) {
+ throw new IllegalArgumentException("Unknown installer package: "
+ + installerPackageName);
+ }
+ }
+
+ Signature[] callerSignature;
+ final int appId = UserHandle.getAppId(callingUid);
+ Pair<PackageStateInternal, SharedUserApi> either =
+ computer.getPackageOrSharedUser(appId);
+ if (either != null) {
+ if (either.first != null) {
+ callerSignature = either.first.getSigningDetails().getSignatures();
+ } else {
+ callerSignature = either.second.getSigningDetails().getSignatures();
+ }
+ } else {
+ throw new SecurityException("Unknown calling UID: " + callingUid);
+ }
+
+ // Verify: can't set installerPackageName to a package that is
+ // not signed with the same cert as the caller.
+ if (installerPackageState != null) {
+ if (compareSignatures(callerSignature,
+ installerPackageState.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as new installer package "
+ + installerPackageName);
+ }
+ }
+
+ // Verify: if target already has an installer package, it must
+ // be signed with the same cert as the caller.
+ String targetInstallerPackageName =
+ targetPackageState.getInstallSource().installerPackageName;
+ PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null
+ ? null : computer.getPackageStateInternal(targetInstallerPackageName);
+
+ if (targetInstallerPkgSetting != null) {
+ if (compareSignatures(callerSignature,
+ targetInstallerPkgSetting.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as old installer package "
+ + targetInstallerPackageName);
+ }
+ } else if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.INSTALL_PACKAGES) != PERMISSION_GRANTED) {
+ // This is probably an attempt to exploit vulnerability b/150857253 of taking
+ // privileged installer permissions when the installer has been uninstalled or
+ // was never set.
+ EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");
+
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ if (mInjector.getCompatibility().isChangeEnabledByUid(
+ PackageManagerService.THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
+ callingUid)) {
+ throw new SecurityException("Neither user " + callingUid
+ + " nor current process has "
+ + Manifest.permission.INSTALL_PACKAGES);
+ } else {
+ // If change disabled, fail silently for backwards compatibility
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ return true;
+ };
+ PackageStateMutator.InitialState initialState = recordInitialState();
+ boolean allowed = implementation.apply(snapshotComputer());
+ if (allowed) {
+ // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
+ // should find an alternative which avoids any race conditions
+ PackageStateInternal targetPackageState;
+ synchronized (mLock) {
+ PackageStateMutator.Result result = commitPackageStateMutation(initialState,
+ targetPackage, state -> state.setInstaller(installerPackageName));
+ if (result.isPackagesChanged() || result.isStateChanged()) {
+ synchronized (mPackageStateWriteLock) {
+ allowed = implementation.apply(snapshotComputer());
+ if (allowed) {
+ commitPackageStateMutation(null, targetPackage,
+ state -> state.setInstaller(installerPackageName));
+ } else {
+ return;
+ }
+ }
+ }
+ targetPackageState = getPackageStateInternal(targetPackage);
+ mSettings.addInstallerPackageNames(targetPackageState.getInstallSource());
+ }
+ mAppsFilter.addPackage(targetPackageState);
+ scheduleWriteSettings();
+ }
+ }
+
+ @Override
+ public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
+ if (HIDE_EPHEMERAL_APIS) {
+ return true;
+ }
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "setInstantAppCookie");
+ if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
+ return false;
+ }
+
+ PackageStateInternal packageState = getPackageStateInternal(packageName);
+ if (packageState == null || packageState.getPkg() == null) {
+ return false;
+ }
+ return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie,
+ mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId);
+ }
+
+ @Override
+ public void setKeepUninstalledPackages(List<String> packageList) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.KEEP_UNINSTALLED_PACKAGES,
+ "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission");
+ Objects.requireNonNull(packageList);
+
+ setKeepUninstalledPackagesInternal(packageList);
+ }
+
+ @Override
+ public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
+ IntentFilter filter, int match, ComponentName activity) {
+ mPreferredActivityHelper.setLastChosenActivity(intent, resolvedType, flags,
+ new WatchedIntentFilter(filter), match, activity);
+ }
+
+ @Override
+ public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
+ enforceOwnerRights(packageName, Binder.getCallingUid());
+ mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
+ if (existingMimeTypes == null) {
+ throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ + " for package " + packageName);
+ }
+ if (existingMimeTypes.size() == mimeTypes.size()
+ && existingMimeTypes.containsAll(mimeTypes)) {
+ return;
+ }
+
+ ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
+ commitPackageStateMutation(null, packageName, packageStateWrite -> {
+ packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
+ });
+ if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
+ Binder.withCleanCallingIdentity(() ->
+ mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+ UserHandle.USER_ALL));
+ }
+
+ scheduleWriteSettings();
+ }
+
+ @Override
+ public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
+ PackageManagerService.this
+ .setPackageStoppedState(snapshotComputer(), packageName, stopped, userId);
+ }
+
+ @Override
+ public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras,
+ SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
+ "setPackagesSuspendedAsUser");
+ return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
+ suspended, appExtras, launcherExtras, dialogInfo, callingPackage, userId,
+ callingUid);
+ }
+
+ @Override
+ public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) {
+ PackageManagerServiceUtils.enforceSystemOrRoot(
+ "setRequiredForSystemUser can only be run by the system or root");
+
+ PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+ packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser));
+ if (!result.isCommitted()) {
+ return false;
+ }
+
+ scheduleWriteSettings();
+ return true;
+ }
+
+ @Override
+ public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
+ Preconditions.checkArgumentNonnegative(version);
+ Preconditions.checkArgumentNonnegative(userId);
+ enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
+ "setRuntimePermissionVersion");
+ mSettings.setDefaultRuntimePermissionsVersion(version, userId);
+ }
+
+ @Override
+ public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "setSplashScreenTheme");
+ enforceOwnerRights(packageName, callingUid);
+
+ PackageStateInternal packageState = getPackageStateInstalledFiltered(packageName,
+ callingUid, userId);
+ if (packageState == null) {
+ return;
+ }
+
+ commitPackageStateMutation(null, packageName, state ->
+ state.userState(userId).setSplashScreenTheme(themeId));
+ }
+
+ @Override
+ public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
+ final int callingUid = Binder.getCallingUid();
+ final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
+ || callingUid == Process.SYSTEM_UID;
+ if (!calledFromSystemOrPhone) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "setSystemAppHiddenUntilInstalled");
+ }
+
+ final PackageStateInternal stateRead = getPackageStateInternal(packageName);
+ if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) {
+ return;
+ }
+ if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
+
+ commitPackageStateMutation(null, mutator -> {
+ mutator.forPackage(packageName)
+ .setHiddenUntilInstalled(hidden);
+ mutator.forDisabledSystemPackage(packageName)
+ .setHiddenUntilInstalled(hidden);
+ });
+ }
+
+ @Override
+ public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
+ || callingUid == Process.SYSTEM_UID;
+ if (!calledFromSystemOrPhone) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "setSystemAppHiddenUntilInstalled");
+ }
+
+ final PackageStateInternal packageState = getPackageStateInternal(packageName);
+ // The target app should always be in system
+ if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
+ return false;
+ }
+ if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
+ // Check if the install state is the same
+ if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
+ return false;
+ }
+
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ if (installed) {
+ // install the app from uninstalled state
+ installExistingPackageAsUser(
+ packageName,
+ userId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_DEVICE_SETUP,
+ null);
+ return true;
+ }
+
+ // uninstall the app from installed state
+ deletePackageVersioned(
+ new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+ new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
+ userId,
+ PackageManager.DELETE_SYSTEM_APP);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
+ public void setUpdateAvailable(String packageName, boolean updateAvailable) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
+ commitPackageStateMutation(null, packageName, state ->
+ state.setUpdateAvailable(updateAvailable));
+ }
+
+ @Override
+ public void unregisterMoveCallback(IPackageMoveObserver callback) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ mMoveCallbacks.unregister(callback);
+ }
+
+ @Deprecated
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
+ }
+
+ @Deprecated
+ @Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
+ DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection,
+ id, verificationCode, failedDomains, Binder.getCallingUid());
+ }
+
+ @Override
+ public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can verify applications");
+ final int callingUid = Binder.getCallingUid();
+
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCode, callingUid);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits,
+ @Checksum.TypeMask int optional, @Checksum.TypeMask int required,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
+ requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
+ onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
+ mInjector.getBackgroundHandler());
+ }
+
+ @Override
+ public void notifyPackagesReplacedReceived(String[] packages) {
+ Computer computer = snapshotComputer();
+ ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
+ for (int index = 0; index < packagesToNotify.size(); index++) {
+ notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */);
+ }
+ }
+
+ @Override
+ public boolean canPackageQuery(@NonNull String sourcePackageName,
+ @NonNull String targetPackageName, @UserIdInt int userId) {
+ return mComputer.canPackageQuery(sourcePackageName, targetPackageName, userId);
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)
+ && !(e instanceof ParcelableException)) {
+ Slog.wtf(TAG, "Package Manager Unexpected Exception", e);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new PackageManagerShellCommand(mIPackageManager,
+ mContext,mDomainVerificationManager.getShell()))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+ new DumpHelper(PackageManagerService.this).doDump(fd, pw, args);
+ }
+ }
+
private class PackageManagerLocalImpl implements PackageManagerLocal {
}
@@ -6855,7 +7173,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isInstantApp(String packageName, int userId) {
- return PackageManagerService.this.isInstantApp(packageName, userId);
+ return PackageManagerService.this.mIPackageManager.isInstantApp(packageName, userId);
}
@Override
@@ -7216,7 +7534,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String getNameForUid(int uid) {
- return PackageManagerService.this.getNameForUid(uid);
+ return mIPackageManager.getNameForUid(uid);
}
@Override
@@ -7461,7 +7779,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isOnlyCoreApps() {
- return PackageManagerService.this.isOnlyCoreApps();
+ return mIPackageManager.isOnlyCoreApps();
}
@Override
@@ -7471,6 +7789,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public void freeAllAppCacheAboveQuota(@NonNull String volumeUuid) throws IOException {
+ PackageManagerService.this.freeAllAppCacheAboveQuota(volumeUuid);
+ }
+
+ @Override
public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
PackageManagerService.this.forEachPackageSetting(actionLocked);
}
@@ -7548,7 +7871,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void finishPackageInstall(int token, boolean didLaunch) {
- PackageManagerService.this.finishPackageInstall(token, didLaunch);
+ mIPackageManager.finishPackageInstall(token, didLaunch);
}
@Nullable
@@ -7793,6 +8116,16 @@ public class PackageManagerService extends IPackageManager.Stub
public Computer snapshot() {
return snapshotComputer();
}
+
+ @Override
+ public void shutdown() {
+ PackageManagerService.this.shutdown();
+ }
+
+ @Override
+ public DynamicCodeLogger getDynamicCodeLogger() {
+ return PackageManagerService.this.getDexManager().getDynamicCodeLogger();
+ }
}
private boolean setEnabledOverlayPackages(@UserIdInt int userId,
@@ -7876,23 +8209,6 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- @Override
- public int getRuntimePermissionsVersion(@UserIdInt int userId) {
- Preconditions.checkArgumentNonnegative(userId);
- enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
- "getRuntimePermissionVersion");
- return mSettings.getDefaultRuntimePermissionsVersion(userId);
- }
-
- @Override
- public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
- Preconditions.checkArgumentNonnegative(version);
- Preconditions.checkArgumentNonnegative(userId);
- enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
- "setRuntimePermissionVersion");
- mSettings.setDefaultRuntimePermissionsVersion(version, userId);
- }
-
private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
@NonNull String message) {
if (mContext.checkCallingOrSelfPermission(
@@ -8021,23 +8337,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
- /**
- * Logs process start information (including base APK hash) to the security log.
- * @hide
- */
- @Override
- public void logAppProcessStartIfNeeded(String packageName, String processName, int uid,
- String seinfo, String apkFile, int pid) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return;
- }
- if (!SecurityLog.isLoggingEnabled()) {
- return;
- }
- mProcessLoggingHandler.logAppProcessStart(mContext, mPmInternal, apkFile, packageName,
- processName, uid, seinfo, pid);
- }
-
public CompilerStats.PackageStats getOrCreateCompilerPackageStats(AndroidPackage pkg) {
return getOrCreateCompilerPackageStats(pkg.getPackageName());
}
@@ -8046,26 +8345,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mCompilerStats.getOrCreatePackageStats(pkgName);
}
- @Override
- public boolean isAutoRevokeWhitelisted(String packageName) {
- int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
- Binder.getCallingUid(), packageName);
- return mode == MODE_IGNORED;
- }
-
- @PackageManager.InstallReason
- @Override
- public int getInstallReason(@NonNull String packageName, @UserIdInt int userId) {
- return mComputer.getInstallReason(packageName, userId);
- }
-
- @Override
- public boolean canRequestPackageInstalls(String packageName, int userId) {
- return mComputer.canRequestPackageInstalls(packageName, Binder.getCallingUid(), userId,
- true /* throwIfPermNotDeclared*/);
- }
-
/**
* Returns true if the system or user is explicitly preventing an otherwise valid installer to
* complete an install. This includes checks like unknown sources and user restrictions.
@@ -8074,45 +8353,35 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.isInstallDisabledForPackage(packageName, uid, userId);
}
- @Override
- public ComponentName getInstantAppResolverSettingsComponent() {
- return mInstantAppResolverSettingsComponent;
- }
-
- @Override
- public ComponentName getInstantAppInstallerComponent() {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
+ private void grantImplicitAccess(@NonNull Computer snapshot, @UserIdInt int userId,
+ Intent intent, @AppIdInt int recipientAppId, int visibleUid, boolean direct,
+ boolean retainOnUpdate) {
+ final AndroidPackage visiblePackage = snapshot.getPackage(visibleUid);
+ final int recipientUid = UserHandle.getUid(userId, recipientAppId);
+ if (visiblePackage == null || snapshot.getPackage(recipientUid) == null) {
+ return;
}
- return mInstantAppInstallerActivity == null
- ? null : mInstantAppInstallerActivity.getComponentName();
- }
- @Override
- public String getInstantAppAndroidId(String packageName, int userId) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
- "getInstantAppAndroidId");
- enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
- false /* checkShell */, "getInstantAppAndroidId");
- // Make sure the target is an Instant App.
- if (!isInstantApp(packageName, userId)) {
- return null;
+ final boolean instantApp = snapshot.isInstantAppInternal(
+ visiblePackage.getPackageName(), userId, visibleUid);
+ final boolean accessGranted;
+ if (instantApp) {
+ if (!direct) {
+ // if the interaction that lead to this granting access to an instant app
+ // was indirect (i.e.: URI permission grant), do not actually execute the
+ // grant.
+ return;
+ }
+ accessGranted = mInstantAppRegistry.grantInstantAccess(userId, intent,
+ recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
+ } else {
+ accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
+ retainOnUpdate);
}
- return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId);
- }
- @Override
- public void grantImplicitAccess(int recipientUid, @NonNull String visibleAuthority) {
- final int callingUid = Binder.getCallingUid();
- final int recipientUserId = UserHandle.getUserId(recipientUid);
- final ProviderInfo providerInfo =
- mComputer.getGrantImplicitAccessProviderInfo(recipientUid, visibleAuthority);
- if (providerInfo == null) {
- return;
+ if (accessGranted) {
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
- int visibleUid = providerInfo.applicationInfo.uid;
- mPmInternal.grantImplicitAccess(recipientUserId, null /*Intent*/,
- UserHandle.getAppId(recipientUid), visibleUid, false /*direct*/);
}
boolean canHaveOatDir(String packageName) {
@@ -8138,100 +8407,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.getUnusedPackages(downgradeTimeThresholdMillis);
}
- @Override
- public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
- int userId) {
- final int callingUid = Binder.getCallingUid();
- final int callingAppId = UserHandle.getAppId(callingUid);
-
- enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
- true /*checkShell*/, "setHarmfulAppInfo");
-
- if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
- checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
- throw new SecurityException("Caller must have the "
- + SET_HARMFUL_APP_WARNINGS + " permission.");
- }
-
- PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
- packageState -> packageState.userState(userId)
- .setHarmfulAppWarning(warning == null ? null : warning.toString()));
- if (result.isSpecificPackageNull()) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- scheduleWritePackageRestrictions(userId);
- }
-
- @Nullable
- @Override
- public CharSequence getHarmfulAppWarning(@NonNull String packageName, @UserIdInt int userId) {
- return mComputer.getHarmfulAppWarning(packageName, userId);
- }
-
- @Override
- public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
- final int callingUid = Binder.getCallingUid();
- final int callingAppId = UserHandle.getAppId(callingUid);
-
- enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
- true /*checkShell*/, "isPackageStateProtected");
-
- if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
- && checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
- throw new SecurityException("Caller must have the "
- + MANAGE_DEVICE_ADMINS + " permission.");
- }
-
- return mProtectedPackages.isPackageStateProtected(userId, packageName);
- }
-
- @Override
- public void sendDeviceCustomizationReadyBroadcast() {
- mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
- "sendDeviceCustomizationReadyBroadcast");
-
- final long ident = Binder.clearCallingIdentity();
- try {
- BroadcastHelper.sendDeviceCustomizationReadyBroadcast();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
- enforceOwnerRights(packageName, Binder.getCallingUid());
- mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
- final PackageStateInternal packageState = getPackageStateInternal(packageName);
- Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
- if (existingMimeTypes == null) {
- throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
- + " for package " + packageName);
- }
- if (existingMimeTypes.size() == mimeTypes.size()
- && existingMimeTypes.containsAll(mimeTypes)) {
- return;
- }
-
- ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
- commitPackageStateMutation(null, packageName, packageStateWrite -> {
- packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
- });
- if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
- Binder.withCleanCallingIdentity(() ->
- mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
- UserHandle.USER_ALL));
- }
-
- scheduleWriteSettings();
- }
-
- @Override
- public List<String> getMimeGroup(String packageName, String mimeGroup) {
- enforceOwnerRights(packageName, Binder.getCallingUid());
- return getMimeGroupInternal(packageName, mimeGroup);
- }
-
private List<String> getMimeGroupInternal(String packageName, String mimeGroup) {
final PackageStateInternal packageState = getPackageStateInternal(packageName);
if (packageState == null) {
@@ -8247,32 +8422,6 @@ public class PackageManagerService extends IPackageManager.Stub
return new ArrayList<>(mimeTypes);
}
- @Override
- public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId,
- int userId) {
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
- false /* checkShell */, "setSplashScreenTheme");
- enforceOwnerRights(packageName, callingUid);
-
- PackageStateInternal packageState = getPackageStateInstalledFiltered(packageName,
- callingUid, userId);
- if (packageState == null) {
- return;
- }
-
- commitPackageStateMutation(null, packageName, state ->
- state.userState(userId).setSplashScreenTheme(themeId));
- }
-
- @Override
- public String getSplashScreenTheme(@NonNull String packageName, int userId) {
- PackageStateInternal packageState =
- getPackageStateInstalledFiltered(packageName, Binder.getCallingUid(), userId);
- return packageState == null ? null
- : packageState.getUserStateOrDefault(userId).getSplashScreenTheme();
- }
-
/**
* Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
* writeLegacyPermissionsTEMP() beforehand.
@@ -8286,21 +8435,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public IBinder getHoldLockToken() {
- if (!Build.IS_DEBUGGABLE) {
- throw new SecurityException("getHoldLockToken requires a debuggable build");
- }
-
- mContext.enforceCallingPermission(
- Manifest.permission.INJECT_EVENTS,
- "getHoldLockToken requires INJECT_EVENTS permission");
-
- final Binder token = new Binder();
- token.attachInterface(this, "holdLock:" + Binder.getCallingUid());
- return token;
- }
-
- @Override
public void verifyHoldLockToken(IBinder token) {
if (!Build.IS_DEBUGGABLE) {
throw new SecurityException("holdLock requires a debuggable build");
@@ -8315,15 +8449,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public void holdLock(IBinder token, int durationMs) {
- mTestUtilityService.verifyHoldLockToken(token);
-
- synchronized (mLock) {
- SystemClock.sleep(durationMs);
- }
- }
-
static String getDefaultTimeouts() {
final long token = Binder.clearCallingIdentity();
try {
@@ -8421,16 +8546,6 @@ public class PackageManagerService extends IPackageManager.Stub
return result.toArray(new PerUidReadTimeouts[result.size()]);
}
- @Override
- public void setKeepUninstalledPackages(List<String> packageList) {
- mContext.enforceCallingPermission(
- Manifest.permission.KEEP_UNINSTALLED_PACKAGES,
- "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission");
- Objects.requireNonNull(packageList);
-
- setKeepUninstalledPackagesInternal(packageList);
- }
-
private void setKeepUninstalledPackagesInternal(List<String> packageList) {
Preconditions.checkNotNull(packageList);
synchronized (mKeepUninstalledPackages) {
@@ -8452,19 +8567,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @Override
- public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
- String featureId, int userId) throws RemoteException {
- return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
- packageName, callingPackage, featureId, userId);
- }
-
- @Override
- public boolean canPackageQuery(@NonNull String sourcePackageName,
- @NonNull String targetPackageName, @UserIdInt int userId) {
- return mComputer.canPackageQuery(sourcePackageName, targetPackageName, userId);
- }
-
boolean getSafeMode() {
return mSafeMode;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 500b4ec70c44..b92f51b392f9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -159,9 +159,9 @@ class PackageManagerShellCommand extends ShellCommand {
private static final SecureRandom RANDOM = new SecureRandom();
- PackageManagerShellCommand(@NonNull PackageManagerService service,
+ PackageManagerShellCommand(@NonNull IPackageManager packageManager,
@NonNull Context context, @NonNull DomainVerificationShell domainVerificationShell) {
- mInterface = service;
+ mInterface = packageManager;
mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
mPermissionManager = context.getSystemService(PermissionManager.class);
mContext = context;
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index fdad83347f24..28ad4b61d8c7 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -38,8 +38,6 @@ final class PackageRemovedInfo {
String mInstallerPackageName;
int mUid = -1;
int mRemovedAppId = -1;
- // If not -1, the app is going through an appId change
- int mNewAppId = -1;
int[] mOrigUsers;
int[] mRemovedUsers = null;
int[] mBroadcastUsers = null;
@@ -67,22 +65,16 @@ final class PackageRemovedInfo {
sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
}
- void sendSystemPackageUpdatedBroadcasts(int newAppId) {
+ void sendSystemPackageUpdatedBroadcasts() {
if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal(newAppId);
+ sendSystemPackageUpdatedBroadcastsInternal();
}
}
- private void sendSystemPackageUpdatedBroadcastsInternal(int newAppId) {
+ private void sendSystemPackageUpdatedBroadcastsInternal() {
Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, newAppId);
- // When appId changes, do not set the replacing extra
- if (mNewAppId >= 0) {
- extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
- extras.putInt(Intent.EXTRA_PREVIOUS_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- } else {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
+ extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
if (mInstallerPackageName != null) {
@@ -90,17 +82,13 @@ final class PackageRemovedInfo {
mRemovedPackage, extras, 0 /*flags*/,
mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
null);
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
}
- if (mNewAppId < 0) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- if (mInstallerPackageName != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- }
- }
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
mRemovedPackage, null, null, null, null /* broadcastAllowList */,
getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
@@ -134,15 +122,10 @@ final class PackageRemovedInfo {
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
-
- // When appId changes, do not set the replacing extra
- if (mNewAppId >= 0) {
- extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
- extras.putInt(Intent.EXTRA_NEW_UID, mNewAppId);
- } else if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
+ final boolean isReplace = mIsUpdate || mIsRemovedPackageSystemUpdate;
+ if (isReplace) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
-
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
if (mRemovedPackage != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
@@ -165,9 +148,9 @@ final class PackageRemovedInfo {
}
}
if (mRemovedAppId >= 0) {
- // If the package is not actually removed, some services need to know the
- // package name affected.
- if (mNewAppId >= 0 || mIsUpdate || mIsRemovedPackageSystemUpdate) {
+ // If a system app's updates are uninstalled the UID is not actually removed. Some
+ // services need to know the package name affected.
+ if (isReplace) {
extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 6b57deba56b5..2016fc3093b3 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -24,7 +24,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
@@ -110,7 +109,7 @@ final class PackageSessionVerifier {
verifyAPK(session, callback);
} catch (PackageManagerException e) {
String errorMessage = PackageManager.installStatusToString(e.error, e.getMessage());
- session.setSessionFailed(SessionInfo.SESSION_VERIFICATION_FAILED, errorMessage);
+ session.setSessionFailed(e.error, errorMessage);
callback.onResult(e.error, e.getMessage());
}
});
@@ -137,7 +136,7 @@ final class PackageSessionVerifier {
}
if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
String errorMessage = PackageManager.installStatusToString(returnCode, msg);
- session.setSessionFailed(SessionInfo.SESSION_VERIFICATION_FAILED, errorMessage);
+ session.setSessionFailed(returnCode, errorMessage);
callback.onResult(returnCode, msg);
} else {
session.setSessionReady();
@@ -220,7 +219,7 @@ final class PackageSessionVerifier {
}
private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
- @SessionInfo.SessionErrorCode int errorCode, String errorMessage) {
+ int errorCode, String errorMessage) {
if (!ensureActiveApexSessionIsAborted(session)) {
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
// Safe to ignore active apex session abortion failure since session will be marked
@@ -312,7 +311,7 @@ final class PackageSessionVerifier {
// Failed to get hold of StorageManager
Slog.e(TAG, "Failed to get hold of StorageManager", e);
throw new PackageManagerException(
- SessionInfo.SESSION_UNKNOWN_ERROR,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"Failed to get hold of StorageManager");
}
// Proactively mark session as ready before calling apexd. Although this call order
@@ -350,7 +349,7 @@ final class PackageSessionVerifier {
final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
input.reset(), apexPath, minSignatureScheme);
if (newResult.isError()) {
- throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to parse APEX package " + apexPath + " : "
+ newResult.getException(), newResult.getException());
}
@@ -369,7 +368,7 @@ final class PackageSessionVerifier {
input.reset(), existingApexPkg.applicationInfo.sourceDir,
SigningDetails.SignatureSchemeVersion.JAR);
if (existingResult.isError()) {
- throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
+ " : " + existingResult.getException(), existingResult.getException());
}
@@ -383,7 +382,7 @@ final class PackageSessionVerifier {
return;
}
- throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"APK-container signature of APEX package " + packageName + " with version "
+ newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
+ " compatible with the one currently installed on device");
@@ -426,11 +425,12 @@ final class PackageSessionVerifier {
packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
if (packageInfo == null) {
throw new PackageManagerException(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Unable to generate package info: " + apexInfo.modulePath);
}
} catch (PackageManagerException e) {
- throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
result.add(packageInfo);
@@ -452,7 +452,7 @@ final class PackageSessionVerifier {
}
}
throw new PackageManagerException(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Could not find rollback id for commit session: " + sessionId);
}
@@ -560,7 +560,7 @@ final class PackageSessionVerifier {
try {
checkActiveSessions(InstallLocationUtils.getStorageManager().supportsCheckpoint());
} catch (RemoteException e) {
- throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"Can't query fs-checkpoint status : " + e);
}
}
@@ -576,7 +576,7 @@ final class PackageSessionVerifier {
}
if (!supportsCheckpoint && activeSessions > 1) {
throw new PackageManagerException(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Cannot stage multiple sessions without checkpoint support");
}
}
@@ -607,13 +607,13 @@ final class PackageSessionVerifier {
// will be deleted.
}
stagedSession.setSessionFailed(
- SessionInfo.SESSION_CONFLICT,
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Session was failed by rollback session: " + session.sessionId());
Slog.i(TAG, "Session " + stagedSession.sessionId() + " is marked failed due to "
+ "rollback session: " + session.sessionId());
} else if (!isRollback(session) && isRollback(stagedSession)) {
throw new PackageManagerException(
- SessionInfo.SESSION_CONFLICT,
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Session was failed by rollback session: " + stagedSession.sessionId());
}
@@ -636,7 +636,7 @@ final class PackageSessionVerifier {
final String packageName = child.getPackageName();
if (packageName == null) {
throw new PackageManagerException(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Cannot stage session " + child.sessionId() + " with package name null");
}
for (StagingManager.StagedSession stagedSession : mStagedSessions) {
@@ -648,14 +648,14 @@ final class PackageSessionVerifier {
if (stagedSession.getCommittedMillis() < parent.getCommittedMillis()) {
// Fail the session committed later when there are overlapping packages
throw new PackageManagerException(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Package: " + packageName + " in session: "
+ child.sessionId()
+ " has been staged already by session: "
+ stagedSession.sessionId());
} else {
stagedSession.setSessionFailed(
- SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Package: " + packageName + " in session: "
+ stagedSession.sessionId()
+ " has been staged already by session: "
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index f06ae1e06187..2bae00f91b82 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -477,6 +477,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
public void setSharedUserAppId(int sharedUserAppId) {
mSharedUserAppId = sharedUserAppId;
+ onChanged();
}
@Override
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 8c49bafa06a8..7253ae4e7cb0 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -151,7 +151,8 @@ final class PreferredActivityHelper {
if (TextUtils.equals(currentPackageName, packageName)) {
return false;
}
- final String[] callingPackages = mPm.getPackagesForUid(Binder.getCallingUid());
+ final String[] callingPackages = mPm.mIPackageManager
+ .getPackagesForUid(Binder.getCallingUid());
if (callingPackages != null && ArrayUtils.contains(callingPackages,
mPm.mRequiredPermissionControllerPackage)) {
// PermissionController manages default home directly.
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 4d1519c361c9..88df843ec8c5 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -284,7 +284,11 @@ final class RemovePackageHelper {
List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
- deletedPs.getPkg(), sharedUserPkgs, UserHandle.USER_ALL);
+ deletedPkg, sharedUserPkgs, UserHandle.USER_ALL);
+ // After permissions are handled, check if the shared user can be migrated
+ if (sus != null) {
+ mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus);
+ }
}
mPm.clearPackagePreferredActivitiesLPw(
deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 33f0b54fb2de..4e8313bf1891 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -165,25 +165,15 @@ final class ScanPackageUtils {
}
}
- int previousAppId = Process.INVALID_UID;
-
if (pkgSetting != null && oldSharedUserSetting != sharedUserSetting) {
- if (oldSharedUserSetting != null && sharedUserSetting == null) {
- previousAppId = pkgSetting.getAppId();
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + oldSharedUserSetting.name + " to " + "<nothing>.");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (oldSharedUserSetting != null
- ? oldSharedUserSetting.name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (oldSharedUserSetting != null
+ ? oldSharedUserSetting.name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
}
String[] usesSdkLibraries = null;
@@ -474,8 +464,8 @@ final class ScanPackageUtils {
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
- previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
+ Process.INVALID_UID /* previousAppId */ , sdkLibraryInfo,
+ staticSharedLibraryInfo, dynamicSharedLibraryInfos);
}
/**
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index f77be1f9b2d3..e2860ca327e7 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -70,7 +70,9 @@ final class ScanResult {
mPkgSetting = pkgSetting;
mChangedAbiCodePath = changedAbiCodePath;
mExistingSettingCopied = existingSettingCopied;
- mPreviousAppId = previousAppId;
+ // Hardcode mPreviousAppId to INVALID_UID (http://b/221088088)
+ // This will disable all migration code paths in PMS and PermMS
+ mPreviousAppId = Process.INVALID_UID;
mSdkSharedLibraryInfo = sdkSharedLibraryInfo;
mStaticSharedLibraryInfo = staticSharedLibraryInfo;
mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 86d7b517ea76..021c3db35756 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -29,6 +29,7 @@ import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1424,6 +1425,35 @@ public final class Settings implements Watchable, Snappable {
}
}
+ /**
+ * Transparently convert a SharedUserSetting into PackageSettings without changing appId.
+ * The sharedUser passed to this method has to be {@link SharedUserSetting#isSingleUser()}.
+ */
+ void convertSharedUserSettingsLPw(SharedUserSetting sharedUser) {
+ final PackageSetting ps = sharedUser.getPackageSettings().valueAt(0);
+ replaceAppIdLPw(sharedUser.getAppId(), ps);
+
+ // Unlink the SharedUserSetting
+ ps.setSharedUserAppId(INVALID_UID);
+ if (!sharedUser.getDisabledPackageSettings().isEmpty()) {
+ final PackageSetting disabledPs = sharedUser.getDisabledPackageSettings().valueAt(0);
+ disabledPs.setSharedUserAppId(INVALID_UID);
+ }
+ mSharedUsers.remove(sharedUser.getName());
+ }
+
+ /**
+ * Check and convert eligible SharedUserSettings to PackageSettings.
+ */
+ void checkAndConvertSharedUserSettingsLPw(SharedUserSetting sharedUser) {
+ if (!sharedUser.isSingleUser()) return;
+ final AndroidPackage pkg = sharedUser.getPackageSettings().valueAt(0).getPkg();
+ if (pkg != null && pkg.isLeavingSharedUid()
+ && SharedUidMigration.applyStrategy(BEST_EFFORT)) {
+ convertSharedUserSettingsLPw(sharedUser);
+ }
+ }
+
PreferredIntentResolver editPreferredActivitiesLPw(int userId) {
PreferredIntentResolver pir = mPreferredActivities.get(userId);
if (pir == null) {
diff --git a/services/core/java/com/android/server/pm/SharedUidMigration.java b/services/core/java/com/android/server/pm/SharedUidMigration.java
new file mode 100644
index 000000000000..e44ef662efff
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedUidMigration.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.IntDef;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.SystemProperties;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A utility class hosting code configuring shared UID migration behavior.
+ */
+public final class SharedUidMigration {
+
+ /**
+ * The system property key used to configure the shared UID migration strategy.
+ */
+ public static final String PROPERTY_KEY = "persist.debug.pm.shared_uid_migration_strategy";
+
+ /**
+ * Only leave shared UID for newly installed packages.
+ */
+ public static final int NEW_INSTALL_ONLY = 1;
+ /**
+ * Opportunistically migrate any single-package shared UID group.
+ */
+ public static final int BEST_EFFORT = 2;
+ /**
+ * Run appId transitions when the device reboots.
+ */
+ public static final int TRANSITION_AT_BOOT = 3;
+ /**
+ * Run appId transitions as soon as the upgrade is installed.
+ */
+ public static final int LIVE_TRANSITION = 4;
+
+ @IntDef({
+ NEW_INSTALL_ONLY,
+ BEST_EFFORT,
+ TRANSITION_AT_BOOT,
+ LIVE_TRANSITION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Strategy {}
+
+ @Strategy
+ private static final int DEFAULT = BEST_EFFORT;
+
+ /**
+ * Whether shared UID migration is fully disabled. Disabled means the sharedUserMaxSdkVersion
+ * attribute will be directly ignored in the parsing phase.
+ */
+ public static boolean isDisabled() {
+ return !PackageManager.ENABLE_SHARED_UID_MIGRATION;
+ }
+
+ /**
+ * If the system is userdebug, returns the strategy to use by getting the system property
+ * {@link #PROPERTY_KEY}, or else returns the default strategy enabled in the build.
+ */
+ public static int getCurrentStrategy() {
+ if (!Build.IS_USERDEBUG) {
+ return DEFAULT;
+ }
+
+ final int s = SystemProperties.getInt(PROPERTY_KEY, DEFAULT);
+ // No transition strategies can be used (http://b/221088088)
+ if (s > BEST_EFFORT || s < NEW_INSTALL_ONLY) {
+ return DEFAULT;
+ }
+ return s;
+ }
+
+ /**
+ * Should the strategy be used based on the current active strategy.
+ */
+ public static boolean applyStrategy(@Strategy int strategy) {
+ return !isDisabled() && getCurrentStrategy() >= strategy;
+ }
+
+ private SharedUidMigration() {}
+}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 4d0bbc6fe437..23f0de8a5f71 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -221,6 +221,25 @@ public final class SharedUserSetting extends SettingBase implements SharedUserAp
}
/**
+ * A shared user is considered "single user" if there is exactly one single package
+ * currently using it. In the case when that package is also a system app, the APK on
+ * the system partition has to also leave shared UID.
+ */
+ public boolean isSingleUser() {
+ if (mPackages.size() != 1) {
+ return false;
+ }
+ if (mDisabledPackages.size() > 1) {
+ return false;
+ }
+ if (mDisabledPackages.size() == 1) {
+ final AndroidPackage pkg = mDisabledPackages.valueAt(0).getPkg();
+ return pkg != null && pkg.isLeavingSharedUid();
+ }
+ return true;
+ }
+
+ /**
* Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo
* to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest
* targetSdkVersion of all apps within the shared user, which corresponds to the least
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 52a7beda43fb..43dde5cce2d6 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,8 +28,6 @@ import android.content.IntentFilter;
import android.content.pm.ApexStagedEvent;
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.StagedApexInfo;
@@ -124,7 +122,7 @@ public class StagingManager {
boolean containsApkSession();
boolean containsApexSession();
void setSessionReady();
- void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage);
+ void setSessionFailed(int errorCode, String errorMessage);
void setSessionApplied();
CompletableFuture<Void> installSession();
boolean hasParentSessionId();
@@ -279,7 +277,7 @@ public class StagingManager {
String packageName = apexSession.getPackageName();
String errorMsg = mApexManager.getApkInApexInstallError(packageName);
if (errorMsg != null) {
- throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED,
+ throw new PackageManagerException(PackageManager.INSTALL_ACTIVATION_FAILED,
"Failed to install apk-in-apex of " + packageName + " : " + errorMsg);
}
}
@@ -392,7 +390,7 @@ public class StagingManager {
revertMsg += " Reason for revert: " + reasonForRevert;
}
Slog.d(TAG, revertMsg);
- session.setSessionFailed(SessionInfo.SESSION_UNKNOWN_ERROR, revertMsg);
+ session.setSessionFailed(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, revertMsg);
return;
}
@@ -477,7 +475,7 @@ public class StagingManager {
for (String apkInApex : mApexManager.getApksInApex(packageName)) {
if (!apkNames.add(apkInApex)) {
throw new PackageManagerException(
- SessionInfo.SESSION_ACTIVATION_FAILED,
+ PackageManager.INSTALL_ACTIVATION_FAILED,
"Package: " + packageName + " in session: "
+ apexSession.sessionId() + " has duplicate apk-in-apex: "
+ apkInApex, null);
@@ -495,9 +493,7 @@ public class StagingManager {
// Should be impossible
throw new RuntimeException(e);
} catch (ExecutionException ee) {
- PackageManagerException e = (PackageManagerException) ee.getCause();
- final String errorMsg = PackageManager.installStatusToString(e.error, e.getMessage());
- throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg);
+ throw (PackageManagerException) ee.getCause();
}
}
@@ -651,7 +647,7 @@ public class StagingManager {
// is upgrading. Fail all the sessions and exit early.
for (int i = 0; i < sessions.size(); i++) {
StagedSession session = sessions.get(i);
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
"Build fingerprint has changed");
}
return;
@@ -691,7 +687,7 @@ public class StagingManager {
final ApexSessionInfo apexSession = apexSessions.get(session.sessionId());
if (apexSession == null || apexSession.isUnknown) {
hasFailedApexSession = true;
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "apexd did "
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED, "apexd did "
+ "not know anything about a staged session supposed to be activated");
continue;
} else if (isApexSessionFailed(apexSession)) {
@@ -707,7 +703,7 @@ public class StagingManager {
errorMsg += " Error: " + apexSession.errorMessage;
}
Slog.d(TAG, errorMsg);
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg);
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED, errorMsg);
continue;
} else if (apexSession.isActivated || apexSession.isSuccess) {
hasAppliedApexSession = true;
@@ -716,13 +712,13 @@ public class StagingManager {
// Apexd did not apply the session for some unknown reason. There is no guarantee
// that apexd will install it next time. Safer to proactively mark it as failed.
hasFailedApexSession = true;
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
"Staged session " + session.sessionId() + " at boot didn't activate nor "
+ "fail. Marking it as failed anyway.");
} else {
Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state");
hasFailedApexSession = true;
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
"Impossible state");
}
}
@@ -742,7 +738,7 @@ public class StagingManager {
// Session has been already failed in the loop above.
continue;
}
- session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+ session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
"Another apex session failed");
}
return;
@@ -758,7 +754,7 @@ public class StagingManager {
} catch (Exception e) {
Slog.e(TAG, "Staged install failed due to unhandled exception", e);
onInstallationFailure(session, new PackageManagerException(
- SessionInfo.SESSION_ACTIVATION_FAILED,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"Staged install failed due to unhandled exception: " + e),
supportsCheckpoint, needsCheckpoint);
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index f07c9eca6a15..88a298a756d4 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -15,6 +15,9 @@
"name": "CtsClassloaderSplitsHostTestCases"
},
{
+ "name": "CtsCompilationTestCases"
+ },
+ {
"name": "CtsAppEnumerationTestCases"
},
{
@@ -111,9 +114,6 @@
},
{
"name": "PackageManagerServiceHostTests"
- },
- {
- "name": "CtsCompilationTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 504769064808..95482d7c7f1a 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -118,8 +118,11 @@ class UserDataPreparer {
flags | StorageManager.FLAG_STORAGE_DE, false);
} else {
try {
- Log.e(TAG, "prepareUserData failed", e);
- RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed");
+ Log.wtf(TAG, "prepareUserData failed for user " + userId, e);
+ if (userId == UserHandle.USER_SYSTEM) {
+ RecoverySystem.rebootPromptAndWipeUserData(mContext,
+ "prepareUserData failed for system user");
+ }
} catch (IOException e2) {
throw new RuntimeException("error rebooting into recovery", e2);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index eb2de6012745..0e6d5e5ed463 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -312,4 +312,12 @@ public abstract class UserManagerInternal {
*/
public abstract void setDefaultCrossProfileIntentFilters(
@UserIdInt int parentUserId, @UserIdInt int profileUserId);
+
+ /**
+ * Returns {@code true} if the system should ignore errors when preparing
+ * the storage directories for the user with ID {@code userId}. This will
+ * return {@code false} for all new users; it will only return {@code true}
+ * for users that already existed on-disk from an older version of Android.
+ */
+ public abstract boolean shouldIgnorePrepareStorageErrors(int userId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d99305d728b9..a8d24fad2598 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -211,6 +211,8 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL =
"lastRequestQuietModeEnabledCall";
+ private static final String TAG_IGNORE_PREPARE_STORAGE_ERRORS =
+ "ignorePrepareStorageErrors";
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m";
@@ -320,6 +322,14 @@ public class UserManagerService extends IUserManager.Stub {
private long mLastRequestQuietModeEnabledMillis;
+ /**
+ * {@code true} if the system should ignore errors when preparing the
+ * storage directories for this user. This is {@code false} for all new
+ * users; it will only be {@code true} for users that already existed
+ * on-disk from an older version of Android.
+ */
+ private boolean mIgnorePrepareStorageErrors;
+
void setLastRequestQuietModeEnabledMillis(long millis) {
mLastRequestQuietModeEnabledMillis = millis;
}
@@ -328,6 +338,25 @@ public class UserManagerService extends IUserManager.Stub {
return mLastRequestQuietModeEnabledMillis;
}
+ boolean getIgnorePrepareStorageErrors() {
+ return mIgnorePrepareStorageErrors;
+ }
+
+ @SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
+ void setIgnorePrepareStorageErrors() {
+ // This method won't be called for new users. But to fully rule out
+ // the possibility of mIgnorePrepareStorageErrors ever being true
+ // for any user on any device that launched with T or later, we also
+ // explicitly check that DEVICE_INITIAL_SDK_INT is below T before
+ // honoring the request to set mIgnorePrepareStorageErrors to true.
+ if (Build.VERSION.DEVICE_INITIAL_SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ mIgnorePrepareStorageErrors = true;
+ return;
+ }
+ Slog.w(LOG_TAG, "Not setting mIgnorePrepareStorageErrors to true"
+ + " since this is a new device");
+ }
+
void clearSeedAccountData() {
seedAccountName = null;
seedAccountType = null;
@@ -3408,6 +3437,10 @@ public class UserManagerService extends IUserManager.Stub {
serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL);
}
+ serializer.startTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS);
+ serializer.text(String.valueOf(userData.getIgnorePrepareStorageErrors()));
+ serializer.endTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS);
+
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -3517,6 +3550,7 @@ public class UserManagerService extends IUserManager.Stub {
Bundle legacyLocalRestrictions = null;
RestrictionsSet localRestrictions = null;
Bundle globalRestrictions = null;
+ boolean ignorePrepareStorageErrors = true; // default is true for old users
final TypedXmlPullParser parser = Xml.resolvePullParser(is);
int type;
@@ -3595,6 +3629,11 @@ public class UserManagerService extends IUserManager.Stub {
if (type == XmlPullParser.TEXT) {
lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText());
}
+ } else if (TAG_IGNORE_PREPARE_STORAGE_ERRORS.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ ignorePrepareStorageErrors = Boolean.parseBoolean(parser.getText());
+ }
}
}
}
@@ -3622,6 +3661,9 @@ public class UserManagerService extends IUserManager.Stub {
userData.persistSeedData = persistSeedData;
userData.seedAccountOptions = seedAccountOptions;
userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp);
+ if (ignorePrepareStorageErrors) {
+ userData.setIgnorePrepareStorageErrors();
+ }
synchronized (mRestrictionsLock) {
if (baseRestrictions != null) {
@@ -5732,6 +5774,9 @@ public class UserManagerService extends IUserManager.Stub {
pw.println();
}
}
+
+ pw.println(" Ignore errors preparing storage: "
+ + userData.getIgnorePrepareStorageErrors());
}
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -6135,6 +6180,14 @@ public class UserManagerService extends IUserManager.Stub {
UserManagerService.this.setDefaultCrossProfileIntentFilters(
profileUserId, userTypeDetails, restrictions, parentUserId);
}
+
+ @Override
+ public boolean shouldIgnorePrepareStorageErrors(int userId) {
+ synchronized (mUsersLock) {
+ UserData userData = mUsers.get(userId);
+ return userData != null && userData.getIgnorePrepareStorageErrors();
+ }
+ }
}
/**
@@ -6297,7 +6350,8 @@ public class UserManagerService extends IUserManager.Stub {
* {@link SecurityException} if not.
*/
private void verifyCallingPackage(String callingPackage, int callingUid) {
- int packageUid = mPm.getPackageUid(callingPackage, 0, UserHandle.getUserId(callingUid));
+ int packageUid = mPm.snapshotComputer()
+ .getPackageUid(callingPackage, 0, UserHandle.getUserId(callingUid));
if (packageUid != callingUid) {
throw new SecurityException("Specified package " + callingPackage
+ " does not match the calling uid " + callingUid);
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 4334cbdce1f2..bc936112649f 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -372,9 +372,10 @@ final class VerificationParams extends HandlerParams {
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
+ final Computer snapshot = mPm.snapshotComputer();
final int requiredUid = requiredVerifierPackage == null ? -1
- : mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
- verifierUserId);
+ : snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
verificationState.setRequiredVerifierUid(requiredUid);
final boolean isVerificationEnabled = isVerificationEnabled(pkgLite,
verifierUserId);
@@ -391,8 +392,8 @@ final class VerificationParams extends HandlerParams {
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Query all live verifiers based on current user state
- final ParceledListSlice<ResolveInfo> receivers = mPm.queryIntentReceivers(verification,
- PACKAGE_MIME_TYPE, 0, verifierUserId);
+ final ParceledListSlice<ResolveInfo> receivers = mPm.queryIntentReceivers(snapshot,
+ verification, PACKAGE_MIME_TYPE, 0, verifierUserId);
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.getList().size() + " verifiers for intent "
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 63469cb24f2f..8d1bcfcb3938 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -122,6 +122,12 @@ public class PackageInfoUtils {
info.isStub = pkg.isStub();
info.coreApp = pkg.isCoreApp();
+ if (pkgSetting != null && !pkgSetting.hasSharedUser()) {
+ // It is possible that this shared UID app has left
+ info.sharedUserId = null;
+ info.sharedUserLabel = 0;
+ }
+
if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
final int N = pkg.getActivities().size();
if (N > 0) {
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 9961ae51b92d..90842619c31e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1403,7 +1403,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
} else {
if (ps.getUserStateOrDefault(userId).isInstantApp() && !bp.isInstant()) {
- throw new SecurityException("Cannot grant non-ephemeral permission" + permName
+ throw new SecurityException("Cannot grant non-ephemeral permission " + permName
+ " for package " + packageName);
}
@@ -3532,8 +3532,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
final Boolean granted =
SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission);
if (granted == null) {
- throw new IllegalStateException("OEM permission" + permission + " requested by package "
- + pkg.getPackageName() + " must be explicitly declared granted or not");
+ throw new IllegalStateException("OEM permission " + permission
+ + " requested by package " + pkg.getPackageName()
+ + " must be explicitly declared granted or not");
}
return Boolean.TRUE == granted;
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index f6f9faf98c40..cbba346ce7b8 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -287,9 +287,6 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setInstallLocation(int installLocation);
- /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */
- ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys);
-
/** @see R#styleable.AndroidManifest_sharedUserMaxSdkVersion */
ParsingPackage setLeavingSharedUid(boolean leavingSharedUid);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 67670272ef8b..1484df8ece14 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -494,7 +494,6 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
ATTRIBUTIONS_ARE_USER_VISIBLE,
RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED,
SDK_LIBRARY,
- INHERIT_KEYSTORE_KEYS,
})
public @interface Values {}
private static final long EXTERNAL_STORAGE = 1L;
@@ -547,9 +546,8 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
private static final long SDK_LIBRARY = 1L << 49;
- private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
- private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 51;
- private static final long LEAVING_SHARED_UID = 1L << 52;
+ private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 50;
+ private static final long LEAVING_SHARED_UID = 1L << 51;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2394,11 +2392,6 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
- public boolean shouldInheritKeyStoreKeys() {
- return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
- }
-
- @Override
public boolean isOnBackInvokedCallbackEnabled() {
return getBoolean(Booleans.ENABLE_ON_BACK_INVOKED_CALLBACK);
}
@@ -2552,11 +2545,6 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
- public ParsingPackageImpl setInheritKeyStoreKeys(boolean value) {
- return setBoolean(Booleans.INHERIT_KEYSTORE_KEYS, value);
- }
-
- @Override
public ParsingPackageImpl setLeavingSharedUid(boolean value) {
return setBoolean(Booleans.LEAVING_SHARED_UID, value);
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
index 50033f652bfd..20b1ed8d0c3e 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -352,11 +352,6 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt
int getLocaleConfigRes();
/**
- * @see R.styleable#AndroidManifest_inheritKeyStoreKeys
- */
- boolean shouldInheritKeyStoreKeys();
-
- /**
* @see R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback
*/
boolean isOnBackInvokedCallbackEnabled();
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index ed1ab01e1d12..112b9e070d28 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -91,6 +91,7 @@ import com.android.internal.R;
import com.android.internal.os.ClassLoaderFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.SharedUidMigration;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
@@ -893,9 +894,7 @@ public class ParsingPackageUtils {
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
- .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
- .setInheritKeyStoreKeys(bool(false,
- R.styleable.AndroidManifest_inheritKeyStoreKeys, sa));
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
boolean foundApp = false;
final int depth = parser.getDepth();
@@ -1047,8 +1046,11 @@ public class ParsingPackageUtils {
}
}
- int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
- boolean leaving = (maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT);
+ boolean leaving = false;
+ if (!SharedUidMigration.isDisabled()) {
+ int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
+ leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT);
+ }
return input.success(pkg
.setLeavingSharedUid(leaving)
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 68e078c519ba..9213c87f12ec 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -48,7 +48,7 @@ public class KeyCombinationManager {
// The rule has been triggered by current keys.
@GuardedBy("mLock")
private TwoKeysCombinationRule mTriggeredRule;
- private final Handler mHandler = new Handler();
+ private final Handler mHandler;
// Keys in a key combination must be pressed within this interval of each other.
private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -93,6 +93,11 @@ public class KeyCombinationManager {
return false;
}
+ // The excessive delay before it dispatching to client.
+ long getKeyInterceptDelayMs() {
+ return COMBINE_KEY_DELAY_MILLIS;
+ }
+
abstract void execute();
abstract void cancel();
@@ -103,7 +108,8 @@ public class KeyCombinationManager {
}
}
- public KeyCombinationManager() {
+ public KeyCombinationManager(Handler handler) {
+ mHandler = handler;
}
void addRule(TwoKeysCombinationRule rule) {
@@ -195,10 +201,18 @@ public class KeyCombinationManager {
*/
long getKeyInterceptTimeout(int keyCode) {
synchronized (mLock) {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
- return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ if (mDownTimes.get(keyCode) == 0) {
+ return 0;
+ }
+ long delayMs = 0;
+ for (final TwoKeysCombinationRule rule : mActiveRules) {
+ if (rule.shouldInterceptKey(keyCode)) {
+ delayMs = Math.max(delayMs, rule.getKeyInterceptDelayMs());
+ }
}
- return 0;
+ // Make sure the delay is less than COMBINE_KEY_DELAY_MILLIS.
+ delayMs = Math.min(delayMs, COMBINE_KEY_DELAY_MILLIS);
+ return mDownTimes.get(keyCode) + delayMs;
}
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 20b7ccd39287..92b9944b74cf 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -57,6 +57,7 @@ public abstract class PermissionPolicyInternal {
* prompt should be shown if the app targets S-, is currently running in a visible, focused
* task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has
* created at least one notification channel (even if it has since been deleted).
+ *
* @param packageName The package whose permission is being checked
* @param userId The user for whom the package is being started
* @param taskId The task the notification prompt should be attached to
@@ -66,10 +67,22 @@ public abstract class PermissionPolicyInternal {
/**
* 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 focused, visible, and running.
+ * prompt. A prompt can be shown if the task is focused, visible, and running and
+ * 1. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
+ * 2. The activity belongs to the same package as the one which launched the task originally,
+ * and the task was started with a launcher intent
+ *
* @param taskInfo The task to be checked
+ * @param currPkg The package of the current top visible activity
+ * @param intent The intent of the current top visible activity
+ */
+ public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
+ @Nullable String currPkg, @Nullable Intent intent);
+
+ /**
+ * @return true if an intent will resolve to a permission request dialog activity
*/
- public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo);
+ public abstract boolean isIntentToPermissionDialog(@NonNull Intent intent);
/**
* @return Whether the policy is initialized for a user.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index f2ce0d4c49d3..70ef3d364277 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -21,6 +21,8 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -54,6 +56,8 @@ import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -109,6 +113,7 @@ public final class PermissionPolicyService extends SystemService {
private static final String SYSTEM_PKG = "android";
private static final boolean DEBUG = false;
private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000;
+ private static final long ACTIVITY_START_DELAY_MS = 200;
private final Object mLock = new Object();
@@ -149,6 +154,7 @@ public final class PermissionPolicyService extends SystemService {
private List<String> mAppOpPermissions;
private Context mContext;
+ private Handler mHandler;
private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManager;
private final KeyguardManager mKeyguardManager;
@@ -158,6 +164,7 @@ public final class PermissionPolicyService extends SystemService {
super(context);
mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
mPackageManager = context.getPackageManager();
mKeyguardManager = context.getSystemService(KeyguardManager.class);
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
@@ -1016,7 +1023,7 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) {
String action = info.intent.getAction();
ActivityInterceptResult result = null;
- if (!PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
+ if (!ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
&& !PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action)) {
return null;
}
@@ -1033,7 +1040,7 @@ public final class PermissionPolicyService extends SystemService {
&& !mContinueNotifGrantMessageUids.contains(info.realCallingUid)) {
return result;
}
- if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
+ if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
String otherPkg = info.intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
if (otherPkg == null || (mPackageManager.getPermissionFlags(
POST_NOTIFICATIONS, otherPkg, UserHandle.of(info.userId))
@@ -1052,8 +1059,8 @@ public final class PermissionPolicyService extends SystemService {
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
- if (!shouldShowNotificationDialogOrClearFlags(info.intent,
- info.checkedOptions)) {
+ if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
+ activityInfo.packageName, info.intent, info.checkedOptions, true)) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1085,7 +1092,7 @@ public final class PermissionPolicyService extends SystemService {
return false;
}
- if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
+ if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
&& (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) {
return false;
}
@@ -1104,18 +1111,48 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId);
}
+ @Override
+ public boolean isIntentToPermissionDialog(@NonNull Intent intent) {
+ return Objects.equals(intent.getPackage(),
+ mPackageManager.getPermissionControllerPackageName())
+ && (Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS_FOR_OTHER)
+ || Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS));
+ }
+
+ @Override
+ public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
+ Intent intent) {
+ return shouldShowNotificationDialogOrClearFlags(
+ taskInfo, currPkg, intent, null, false);
+ }
+
/**
- * Determine if we should show a notification dialog, or clear the REVIEW_REQUIRED flag,
- * from a particular package for a particular intent. Returns true if:
+ * 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
+ * currently focused, visible, and running, and,
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
- * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
+ * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
+ * 3. The activity belongs to the same package as the one which launched the task
+ * originally, and the task was started with a launcher intent
+ * @param taskInfo The task to be checked
+ * @param currPkg The package of the current top visible activity
+ * @param intent The intent of the current top visible activity
*/
- private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
- ActivityOptions options) {
- if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
- return true;
+ private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
+ Intent intent, ActivityOptions options, boolean activityStart) {
+ if (intent == null || currPkg == null || taskInfo == null
+ || (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
+ && !activityStart)) {
+ return false;
}
+ return isLauncherIntent(intent)
+ || (options != null && options.isEligibleForLegacyPermissionPrompt())
+ || (currPkg.equals(taskInfo.baseActivity.getPackageName())
+ && isLauncherIntent(taskInfo.baseIntent));
+ }
+
+ private boolean isLauncherIntent(Intent intent) {
return Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.getCategories() != null
&& (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
@@ -1144,14 +1181,15 @@ public final class PermissionPolicyService extends SystemService {
Intent grantPermission = mPackageManager
.buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
grantPermission.setAction(
- PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
+ ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
ActivityOptions options = new ActivityOptions(new Bundle());
options.setTaskOverlay(true, false);
options.setLaunchTaskId(taskId);
try {
- mContext.startActivityAsUser(grantPermission, options.toBundle(), user);
+ mHandler.postDelayed(() -> mContext.startActivityAsUser(
+ grantPermission, options.toBundle(), user), ACTIVITY_START_DELAY_MS);
} catch (Exception e) {
Log.e(LOG_TAG, "couldn't start grant permission dialog"
+ "for other package " + pkgName, e);
@@ -1170,12 +1208,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- @Override
- public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) {
- return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible
- && taskInfo.isRunning;
- }
-
/**
* Check if the intent action is removed for the calling package (often based on target SDK
* version). If the action is removed, we'll silently cancel the activity launch.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cc93d4c1c3ff..e79148d52715 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2124,7 +2124,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void initKeyCombinationRules() {
- mKeyCombinationManager = new KeyCombinationManager();
+ mKeyCombinationManager = new KeyCombinationManager(mHandler);
final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableScreenshotChord);
@@ -2215,11 +2215,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mBackKeyHandled = true;
interceptAccessibilityGestureTv();
}
-
@Override
void cancel() {
cancelAccessibilityGestureTv();
}
+ @Override
+ long getKeyInterceptDelayMs() {
+ // Use a timeout of 0 to prevent additional latency in processing of
+ // this key. This will potentially cause some unwanted UI actions if the
+ // user does end up triggering the key combination later, but in most
+ // cases, the user will simply hit a single key, and this will allow us
+ // to process it without first waiting to see if the combination is
+ // going to be triggered.
+ return 0;
+ }
});
mKeyCombinationManager.addRule(
@@ -2229,11 +2238,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mBackKeyHandled = true;
interceptBugreportGestureTv();
}
-
@Override
void cancel() {
cancelBugreportGestureTv();
}
+ @Override
+ long getKeyInterceptDelayMs() {
+ return 0;
+ }
});
}
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index dcfb8b5e7b33..a82d4eaa5b28 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.media.AudioAttributes;
import android.os.FileUtils;
import android.os.Handler;
@@ -525,8 +526,7 @@ public final class ShutdownThread extends Thread {
shutdownTimingLog.traceBegin("ShutdownPackageManager");
metricStarted(METRIC_PM);
- final PackageManagerService pm = (PackageManagerService)
- ServiceManager.getService("package");
+ final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
if (pm != null) {
pm.shutdown();
}
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index c0207f044b83..16592d764e64 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -24,6 +24,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Environment;
import android.os.FileObserver;
@@ -31,7 +32,6 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -49,6 +49,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.PackageManagerService;
@@ -187,10 +188,10 @@ public class DeviceStorageMonitorService extends SystemService {
// when it's within 150% of the threshold, we try trimming usage
// back to 200% of the threshold.
if (file.getUsableSpace() < (lowBytes * 3) / 2) {
- final PackageManagerService pms = (PackageManagerService) ServiceManager
- .getService("package");
+ final PackageManagerInternal pm =
+ LocalServices.getService(PackageManagerInternal.class);
try {
- pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
+ pm.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
} catch (IOException e) {
Slog.w(TAG, e);
}
@@ -264,10 +265,10 @@ public class DeviceStorageMonitorService extends SystemService {
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final File file = vol.getPath();
if (file.getUsableSpace() < file.getTotalSpace() * storageThresholdPercentHigh / 100) {
- final PackageManagerService pms = (PackageManagerService) ServiceManager
- .getService("package");
+ final PackageManagerInternal pm =
+ LocalServices.getService(PackageManagerInternal.class);
try {
- pms.freeAllAppCacheAboveQuota(vol.getFsUuid());
+ pm.freeAllAppCacheAboveQuota(vol.getFsUuid());
} catch (IOException e) {
Slog.w(TAG, e);
}
diff --git a/services/core/java/com/android/server/trust/TEST_MAPPING b/services/core/java/com/android/server/trust/TEST_MAPPING
new file mode 100644
index 000000000000..be8ed67f459b
--- /dev/null
+++ b/services/core/java/com/android/server/trust/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "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/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index b05b44bcb1d2..77da75118958 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -355,8 +355,10 @@ final class VibrationSettings {
}
}
- if (!shouldVibrateForRingerModeLocked(usage)) {
- return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ if (!shouldVibrateForRingerModeLocked(usage)) {
+ return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ }
}
}
return null;
@@ -386,12 +388,12 @@ final class VibrationSettings {
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
- * for ringtone usage only. All other usages are allowed by this method.
+ * for ringtone and notification usages. All other usages are allowed by this method.
*/
@GuardedBy("mLock")
private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
- if (usageHint != USAGE_RINGTONE) {
- // Only ringtone vibrations are disabled when phone is on silent mode.
+ if ((usageHint != USAGE_RINGTONE) && (usageHint != USAGE_NOTIFICATION)) {
+ // Only ringtone and notification vibrations are disabled when phone is on silent mode.
return true;
}
// If audio manager was not loaded yet then assume most restrictive mode.
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 58407cf7fd46..e12426b2b02c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -60,8 +60,6 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
- private final Object mLock = new Object();
-
// Used within steps.
public final VibrationSettings vibrationSettings;
public final DeviceVibrationEffectAdapter deviceEffectAdapter;
@@ -74,6 +72,11 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
// Signalling fields.
+ // Note that vibrator callback signals may happen inside vibrator HAL calls made by the
+ // VibrationThread, or on an external executor, so this lock should not be held for anything
+ // other than updating signalling state - particularly not during HAL calls or when invoking
+ // other callbacks that may trigger calls into the thread.
+ private final Object mLock = new Object();
@GuardedBy("mLock")
private final IntArray mSignalVibratorsComplete;
@GuardedBy("mLock")
@@ -334,9 +337,9 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
* The state update is recorded for processing on the main execution thread (VibrationThread).
*/
public void notifyVibratorComplete(int vibratorId) {
- if (Build.IS_DEBUGGABLE) {
- expectIsVibrationThread(false);
- }
+ // HAL callbacks may be triggered directly within HAL calls, so these notifications
+ // could be on the VibrationThread as it calls the HAL, or some other executor later.
+ // Therefore no thread assertion is made here.
if (DEBUG) {
Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
@@ -356,9 +359,9 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
* (VibrationThread).
*/
public void notifySyncedVibrationComplete() {
- if (Build.IS_DEBUGGABLE) {
- expectIsVibrationThread(false);
- }
+ // HAL callbacks may be triggered directly within HAL calls, so these notifications
+ // could be on the VibrationThread as it calls the HAL, or some other executor later.
+ // Therefore no thread assertion is made here.
if (DEBUG) {
Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
@@ -394,7 +397,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
int[] vibratorsToProcess = null;
boolean doCancel = false;
boolean doCancelImmediate = false;
- // Swap out the queue of completions to process.
+ // Collect signals to process, but don't keep the lock while processing them.
synchronized (mLock) {
if (mSignalCancelImmediate) {
if (mCancelledImmediately) {
@@ -407,6 +410,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
doCancel = true;
}
if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
+ // Swap out the queue of completions to process.
vibratorsToProcess = mSignalVibratorsComplete.toArray(); // makes a copy
mSignalVibratorsComplete.clear();
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 205ea62496ff..cecc5c04dedc 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -22,6 +22,7 @@ import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.WorkSource;
import android.util.Slog;
@@ -176,7 +177,7 @@ final class VibrationThread extends Thread {
* @return true if the vibration completed, or false if waiting timed out.
*/
public boolean waitForThreadIdle(long maxWaitMillis) {
- long now = System.currentTimeMillis();
+ long now = SystemClock.elapsedRealtime();
long deadline = now + maxWaitMillis;
synchronized (mLock) {
while (true) {
@@ -191,7 +192,7 @@ final class VibrationThread extends Thread {
} catch (InterruptedException e) {
Slog.w(TAG, "VibrationThread interrupted waiting to stop, continuing");
}
- now = System.currentTimeMillis();
+ now = SystemClock.elapsedRealtime();
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9ec1079e46cc..1260e5dbc9f7 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -464,10 +464,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
- endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
mCurrentExternalVibration.externalVibration.mute();
- mCurrentExternalVibration = null;
- setExternalControl(false);
+ endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ /* continueExternalControl= */ false);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1283,7 +1282,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/** Holder for a {@link ExternalVibration}. */
- private final class ExternalVibrationHolder {
+ private final class ExternalVibrationHolder implements IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
public int scale;
@@ -1308,6 +1307,18 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mEndTimeDebug = System.currentTimeMillis();
}
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "External vibration finished because binder died");
+ }
+ endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ /* continueExternalControl= */ false);
+ }
+ }
+ }
+
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(
mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
@@ -1450,10 +1461,36 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /**
+ * Ends the external vibration, and clears related service state.
+ *
+ * @param status the status to end the associated Vibration with
+ * @param continueExternalControl indicates whether external control will continue. If not, the
+ * HAL will have external control turned off.
+ */
+ @GuardedBy("mLock")
+ private void endExternalVibrateLocked(Vibration.Status status,
+ boolean continueExternalControl) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "endExternalVibrateLocked");
+ try {
+ if (mCurrentExternalVibration == null) {
+ return;
+ }
+ endVibrationLocked(mCurrentExternalVibration, status);
+ mCurrentExternalVibration.externalVibration.unlinkToDeath(
+ mCurrentExternalVibration);
+ mCurrentExternalVibration = null;
+ if (!continueExternalControl) {
+ setExternalControl(false);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
@VisibleForTesting
final class ExternalVibratorService extends IExternalVibratorService.Stub {
- ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
@Override
public int onExternalVibrationStart(ExternalVibration vib) {
@@ -1469,7 +1506,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return IExternalVibratorService.SCALE_MUTE;
}
- ExternalVibrationHolder cancelingExternalVibration = null;
+ boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
int scale;
synchronized (mLock) {
@@ -1504,13 +1541,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
//
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
+ alreadyUnderExternalControl = true;
mCurrentExternalVibration.externalVibration.mute();
- endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
- cancelingExternalVibration = mCurrentExternalVibration;
+ endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ /* continueExternalControl= */ true);
}
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
- mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
- vib.linkToDeath(mCurrentExternalDeathRecipient);
+ vib.linkToDeath(mCurrentExternalVibration);
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
vib.getVibrationAttributes().getUsage());
scale = mCurrentExternalVibration.scale;
@@ -1520,14 +1557,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
Slog.e(TAG, "Timed out waiting for vibration to cancel");
synchronized (mLock) {
- stopExternalVibrateLocked(Vibration.Status.IGNORED_ERROR_CANCELLING);
+ endExternalVibrateLocked(Vibration.Status.IGNORED_ERROR_CANCELLING,
+ /* continueExternalControl= */ false);
}
return IExternalVibratorService.SCALE_MUTE;
}
}
- if (cancelingExternalVibration == null) {
- // We only need to set external control if it was not already set by another
- // external vibration.
+ if (!alreadyUnderExternalControl) {
if (DEBUG) {
Slog.d(TAG, "Vibrator going under external control.");
}
@@ -1547,29 +1583,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.e(TAG, "Stopping external vibration" + vib);
}
- stopExternalVibrateLocked(Vibration.Status.FINISHED);
+ endExternalVibrateLocked(Vibration.Status.FINISHED,
+ /* continueExternalControl= */ false);
}
}
}
- @GuardedBy("mLock")
- private void stopExternalVibrateLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "stopExternalVibrateLocked");
- try {
- if (mCurrentExternalVibration == null) {
- return;
- }
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.unlinkToDeath(
- mCurrentExternalDeathRecipient);
- mCurrentExternalDeathRecipient = null;
- mCurrentExternalVibration = null;
- setExternalControl(false);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
private boolean hasExternalControlCapability() {
for (int i = 0; i < mVibrators.size(); i++) {
if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
@@ -1578,19 +1597,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
return false;
}
-
- private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
- public void binderDied() {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "External vibration finished because binder died");
- }
- stopExternalVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
- }
}
/** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index f9689a8a2bca..d4648a4f313c 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -27,6 +27,7 @@ import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Looper;
import android.os.Message;
import android.util.Slog;
@@ -153,7 +154,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
final List<InputWindowHandle> tempVisibleWindows = new ArrayList<>();
for (InputWindowHandle window : windowHandles) {
- if (window.visible && window.getWindow() != null) {
+ final boolean visible = (window.inputConfig & InputConfig.NOT_VISIBLE) == 0;
+ if (visible && window.getWindow() != null) {
tempVisibleWindows.add(window);
}
}
@@ -641,7 +643,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
final RecentsAnimationController controller = service.getRecentsAnimationController();
instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
&& controller.shouldIgnoreForAccessibility(windowState);
- instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay;
+ instance.mIsTrustedOverlay =
+ (inputWindowHandle.inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
// TODO (b/199358388) : gets the letterbox bounds of the window from other way.
if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) {
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 06c58baee1f9..1d65cbb70ffe 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -58,6 +58,7 @@ public abstract class ActivityInterceptorCallback {
@IntDef(suffix = { "_ORDERED_ID" }, value = {
FIRST_ORDERED_ID,
PERMISSION_POLICY_ORDERED_ID,
+ INTENT_RESOLVER_ORDERED_ID,
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
LAST_ORDERED_ID // Update this when adding new ids
})
@@ -75,6 +76,11 @@ public abstract class ActivityInterceptorCallback {
public static final int PERMISSION_POLICY_ORDERED_ID = 1;
/**
+ * The identifier for {@link com.android.server.pm.IntentResolverInterceptor}.
+ */
+ public static final int INTENT_RESOLVER_ORDERED_ID = 2;
+
+ /**
* The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
* interceptor.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e66f309d2ccc..2153f5f4dd70 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -278,6 +278,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.gui.DropInputMode;
import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Binder;
@@ -782,6 +783,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean startingDisplayed;
boolean startingMoved;
+ /** The last set {@link DropInputMode} for this activity surface. */
+ @DropInputMode
+ private int mLastDropInputMode = DropInputMode.NONE;
+
/**
* If it is non-null, it requires all activities who have the same starting data to be drawn
* to remove the starting window.
@@ -1548,6 +1553,60 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
rootTask.setHasBeenVisible(true);
}
}
+
+ // Update the input mode if the embedded mode is changed.
+ updateUntrustedEmbeddingInputProtection();
+ }
+
+ @Override
+ void setSurfaceControl(SurfaceControl sc) {
+ super.setSurfaceControl(sc);
+ if (sc != null) {
+ mLastDropInputMode = DropInputMode.NONE;
+ updateUntrustedEmbeddingInputProtection();
+ }
+ }
+
+ /**
+ * Sets to drop input when obscured to activity if it is embedded in untrusted mode.
+ *
+ * Although the untrusted embedded activity should be invisible when behind other overlay,
+ * theoretically even if this activity is the top most, app can still move surface of activity
+ * below it to the top. As a result, we want to update the input mode to drop when obscured for
+ * all untrusted activities.
+ */
+ private void updateUntrustedEmbeddingInputProtection() {
+ final SurfaceControl sc = getSurfaceControl();
+ if (sc == null) {
+ return;
+ }
+ if (isEmbeddedInUntrustedMode()) {
+ // Set drop input to OBSCURED when untrusted embedded.
+ setDropInputMode(DropInputMode.OBSCURED);
+ } else {
+ // Reset drop input mode when this activity is not embedded in untrusted mode.
+ setDropInputMode(DropInputMode.NONE);
+ }
+ }
+
+ @VisibleForTesting
+ void setDropInputMode(@DropInputMode int mode) {
+ if (mLastDropInputMode != mode && getSurfaceControl() != null) {
+ mLastDropInputMode = mode;
+ mWmService.mTransactionFactory.get()
+ .setDropInputMode(getSurfaceControl(), mode)
+ .apply();
+ }
+ }
+
+ private boolean isEmbeddedInUntrustedMode() {
+ final TaskFragment organizedTaskFragment = getOrganizedTaskFragment();
+ if (organizedTaskFragment == null) {
+ // Not embedded.
+ return false;
+ }
+ // Check if trusted.
+ return !organizedTaskFragment.isAllowedToEmbedActivityInTrustedMode(this);
}
void updateAnimatingActivityRegistry() {
@@ -2452,7 +2511,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// either way, abort and reset the sequence.
if (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
- || mStartingWindow == null) {
+ || mStartingWindow == null
+ || finishing) {
if (parcelable != null) {
parcelable.clearIfNeeded();
}
@@ -3642,6 +3702,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
finishing = true;
+
+ // Transfer the launch cookie to the next running activity above this in the same task.
+ if (mLaunchCookie != null && mState != RESUMED && task != null && !task.mInRemoveTask
+ && !task.isClearingToReuseTask()) {
+ final ActivityRecord nextCookieTarget = task.getActivity(
+ // Intend to only associate the same app by checking uid.
+ r -> r.mLaunchCookie == null && !r.finishing && r.isUid(getUid()),
+ this, false /* includeBoundary */, false /* traverseTopToBottom */);
+ if (nextCookieTarget != null) {
+ nextCookieTarget.mLaunchCookie = mLaunchCookie;
+ mLaunchCookie = null;
+ }
+ }
+
final TaskFragment taskFragment = getTaskFragment();
if (taskFragment != null) {
final Task task = taskFragment.getTask();
@@ -5428,6 +5502,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
+ // Untrusted embedded activity can be visible only if there is no other overlay window.
+ if (hasOverlayOverUntrustedModeEmbedded()) {
+ return false;
+ }
+
// Check if the activity is on a sleeping display, canTurnScreenOn will also check
// keyguard visibility
if (mDisplayContent.isSleeping()) {
@@ -5437,6 +5516,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /**
+ * Checks if there are any activities or other containers that belong to the same task on top of
+ * this activity when embedded in untrusted mode.
+ */
+ boolean hasOverlayOverUntrustedModeEmbedded() {
+ if (!isEmbeddedInUntrustedMode() || getRootTask() == null) {
+ // The activity is not embedded in untrusted mode.
+ return false;
+ }
+
+ // Check if there are any activities with different UID over the activity that is embedded
+ // in untrusted mode. Traverse bottom to top with boundary so that it will only check
+ // activities above this activity.
+ final ActivityRecord differentUidOverlayActivity = getRootTask().getActivity(
+ a -> a.getUid() != getUid(), this /* boundary */, false /* includeBoundary */,
+ false /* traverseTopToBottom */);
+ return differentUidOverlayActivity != null;
+ }
+
void updateVisibilityIgnoringKeyguard(boolean behindFullscreenActivity) {
visibleIgnoringKeyguard = (!behindFullscreenActivity || mLaunchTaskBehind)
&& showToCurrentUser();
@@ -6669,7 +6767,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
// not specified in the ActivityOptions.
- if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME) {
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
+ || launchedFromUid == Process.SHELL_UID) {
return false;
} else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
return true;
@@ -6689,7 +6788,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// solid color splash screen.
// Need to check sourceRecord before in case this activity is launched from service.
return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
- || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME);
+ || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
+ || launchedFromUid == Process.SHELL_UID);
}
private int getSplashscreenTheme(ActivityOptions options) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 19d5449254ff..8622bd32fc68 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.InputConstants;
import android.os.Looper;
import android.os.Process;
@@ -111,13 +112,12 @@ class ActivityRecordInputSink {
mActivityRecord.getDisplayId());
inputWindowHandle.replaceTouchableRegionWithCrop = true;
inputWindowHandle.name = mName;
+ inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
inputWindowHandle.ownerUid = Process.myUid();
inputWindowHandle.ownerPid = Process.myPid();
- inputWindowHandle.layoutParamsFlags =
- WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
inputWindowHandle.dispatchingTimeoutMillis =
InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
return inputWindowHandle;
}
@@ -169,5 +169,4 @@ class ActivityRecordInputSink {
finishInputEvent(event, true);
}
}
-
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index c5fcd12efa78..cdeb86c3ee77 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -355,10 +355,10 @@ class ActivityStartInterceptor {
* @return The intercepting intent if needed.
*/
private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
- if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
+ if ((aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0
+ || !mService.mAmInternal.shouldConfirmCredentials(userId)) {
return null;
}
- // TODO(b/28935539): should allow certain activities to bypass work challenge
final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
final KeyguardManager km = (KeyguardManager) mServiceContext
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index fd2afd47bec4..ac121a13f308 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1816,7 +1816,8 @@ class ActivityStarter {
}
if (mTargetRootTask == null) {
- mTargetRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags, targetTask, mOptions);
+ mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
+ mOptions);
}
if (newTask) {
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
@@ -1925,7 +1926,7 @@ class ActivityStarter {
} else if (mInTask != null) {
return mInTask;
} else {
- final Task rootTask = getLaunchRootTask(mStartActivity, mLaunchFlags, null /* task */,
+ final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
mOptions);
final ActivityRecord top = rootTask.getTopNonFinishingActivity();
if (top != null) {
@@ -2233,7 +2234,7 @@ class ActivityStarter {
if (targetTask.getRootTask() == null) {
// Target root task got cleared when we all activities were removed above.
// Go ahead and reset it.
- mTargetRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags,
+ mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags,
null /* task */, mOptions);
mTargetRootTask.addChild(targetTask, !mLaunchTaskBehind /* toTop */,
(mStartActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
@@ -2695,10 +2696,11 @@ class ActivityStarter {
// launched into the same root task.
mTargetRootTask = Task.fromWindowContainerToken(mSourceRecord.mLaunchRootTask);
} else {
- final Task launchRootTask =
- getLaunchRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions);
+ final Task rootTask =
+ getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions);
+ // TODO(b/184806710): #getOrCreateRootTask should never return null?
mTargetRootTask =
- launchRootTask != null ? launchRootTask : intentActivity.getRootTask();
+ rootTask != null ? rootTask : intentActivity.getRootTask();
}
}
@@ -2929,7 +2931,7 @@ class ActivityStarter {
return launchFlags;
}
- private Task getLaunchRootTask(ActivityRecord r, int launchFlags, Task task,
+ private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
ActivityOptions aOptions) {
// We are reusing a task, keep the root task!
if (mReuseTask != null) {
@@ -2938,7 +2940,7 @@ class ActivityStarter {
final boolean onTop =
(aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
- return mRootWindowContainer.getLaunchRootTask(r, aOptions, task, mSourceRootTask, onTop,
+ return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, mSourceRootTask, onTop,
mLaunchParams, launchFlags);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index cecfccd1f836..01dfb91d12be 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -680,4 +680,15 @@ public abstract class ActivityTaskManagerInternal {
/** Get the app tasks for a package */
public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid);
+
+ /**
+ * Determine if there exists a task which meets the criteria set by the PermissionPolicyService
+ * to show a system-owned permission dialog over, for a given package
+ * @see PermissionPolicyInternal.shouldShowNotificationDialogForTask
+ *
+ * @param pkgName The package whose activity must be top
+ * @param uid The uid that must have a top activity
+ * @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID
+ */
+ public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bfccdf97c680..6d8b3b1d63dd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6754,5 +6754,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return tasks;
}
+
+ @Override
+ public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
+ synchronized (ActivityTaskManagerService.this.mGlobalLock) {
+ return ActivityTaskManagerService.this.mRootWindowContainer
+ .getTaskToShowPermissionDialogOn(pkgName, uid);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5573f161d1c1..64f426187fd2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -954,7 +954,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// This is the first time we failed -- restart process and
// retry.
r.launchFailed = true;
- proc.removeActivity(r, true /* keepAssociation */);
+ r.detachFromProcess();
throw e;
}
} finally {
@@ -1046,6 +1046,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// If a dead object exception was thrown -- fall through to
// restart the application.
knownToBeDead = true;
+ // Remove the process record so it won't be considered as alive.
+ mService.mProcessNames.remove(wpc.mName, wpc.mUid);
+ mService.mProcessMap.remove(wpc.getPid());
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
@@ -1436,20 +1439,20 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
final Rect bounds = options.getLaunchBounds();
task.setBounds(bounds);
- Task launchRootTask =
- mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP);
+ Task targetRootTask =
+ mRootWindowContainer.getOrCreateRootTask(null, options, task, ON_TOP);
- if (launchRootTask != currentRootTask) {
- moveHomeRootTaskToFrontIfNeeded(flags, launchRootTask.getDisplayArea(), reason);
- task.reparent(launchRootTask, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+ if (targetRootTask != currentRootTask) {
+ moveHomeRootTaskToFrontIfNeeded(flags, targetRootTask.getDisplayArea(), reason);
+ task.reparent(targetRootTask, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
!ANIMATE, DEFER_RESUME, reason);
- currentRootTask = launchRootTask;
+ currentRootTask = targetRootTask;
reparented = true;
// task.reparent() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
- if (launchRootTask.shouldResizeRootTaskWithLaunchBounds()) {
- launchRootTask.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
+ if (targetRootTask.shouldResizeRootTaskWithLaunchBounds()) {
+ targetRootTask.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1693,7 +1696,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
*/
boolean restoreRecentTaskLocked(Task task, ActivityOptions aOptions, boolean onTop) {
final Task rootTask =
- mRootWindowContainer.getLaunchRootTask(null, aOptions, task, onTop);
+ mRootWindowContainer.getOrCreateRootTask(null, aOptions, task, onTop);
final WindowContainer parent = task.getParent();
if (parent == rootTask || task == rootTask) {
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 9e889ad11b8e..220d9ec8febb 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -329,8 +329,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
void hideImmediately(WindowToken windowToken) {
final boolean original = mHideImmediately;
mHideImmediately = true;
+ final Operation op = new Operation(Operation.ACTION_FADE);
+ mTargetWindowTokens.put(windowToken, op);
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ op.mLeash = windowToken.getAnimationLeash();
mHideImmediately = original;
+ if (DEBUG) Slog.d(TAG, "hideImmediately " + windowToken.getTopChild());
}
/** Returns {@code true} if the window will rotate independently. */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 718a28de6af9..d1b9a7895058 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -131,6 +131,7 @@ import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -627,9 +628,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@VisibleForTesting
SurfaceControl mInputMethodSurfaceParent;
- /** The screenshot IME surface to place on the task while transitioning to the next task. */
- SurfaceControl mImeScreenshot;
-
private final PointerEventDispatcher mPointerEventDispatcher;
private final InsetsStateController mInsetsStateController;
@@ -3974,10 +3972,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// If the IME target is the input target, before it changes, prepare the IME screenshot
// for the last IME target when its task is applying app transition. This is for the
// better IME transition to keep IME visibility when transitioning to the next task.
- if (mImeLayeringTarget != null && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
- && mImeLayeringTarget == mImeInputTarget) {
- attachAndShowImeScreenshotOnTarget();
+ if (mImeLayeringTarget != null && mImeLayeringTarget == mImeInputTarget) {
+ boolean nonAppImeTargetAnimatingExit = mImeLayeringTarget.mAnimatingExit
+ && mImeLayeringTarget.mAttrs.type != TYPE_BASE_APPLICATION
+ && mImeLayeringTarget.isSelfAnimating(0, ANIMATION_TYPE_WINDOW_ANIMATION);
+ if (mImeLayeringTarget.inAppOrRecentsTransition() || nonAppImeTargetAnimatingExit) {
+ showImeScreenshot();
+ }
}
ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target);
@@ -4025,71 +4026,129 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mImeControlTarget = target;
}
- @VisibleForTesting
- void attachAndShowImeScreenshotOnTarget() {
- // No need to attach screenshot if the IME target not exists or screen is off.
- if (!shouldImeAttachedToApp() || !mWmService.mPolicy.isScreenOn()) {
- return;
+ // ========== Begin of ImeScreenshot stuff ==========
+ /** The screenshot IME surface to place on the task while transitioning to the next task. */
+ ImeScreenshot mImeScreenshot;
+
+ static final class ImeScreenshot {
+ private WindowState mImeTarget;
+ private SurfaceControl.Builder mSurfaceBuilder;
+ private SurfaceControl mImeSurface;
+
+ ImeScreenshot(SurfaceControl.Builder surfaceBuilder, @NonNull WindowState imeTarget) {
+ mSurfaceBuilder = surfaceBuilder;
+ mImeTarget = imeTarget;
}
- final SurfaceControl.Transaction t = getPendingTransaction();
- // Prepare IME screenshot for the target if it allows to attach into.
- if (mInputMethodWindow != null && mInputMethodWindow.isVisible()) {
- final Task task = mImeLayeringTarget.getTask();
+ WindowState getImeTarget() {
+ return mImeTarget;
+ }
+
+ private SurfaceControl createImeSurface(SurfaceControl.ScreenshotHardwareBuffer b,
+ Transaction t) {
+ final HardwareBuffer buffer = b.getHardwareBuffer();
+ if (DEBUG_INPUT_METHOD) {
+ Slog.d(TAG, "create IME snapshot for "
+ + mImeTarget + ", buff width=" + buffer.getWidth()
+ + ", height=" + buffer.getHeight());
+ }
+ final WindowState imeWindow = mImeTarget.getDisplayContent().mInputMethodWindow;
+ final ActivityRecord activity = mImeTarget.mActivityRecord;
+ final SurfaceControl imeParent = mImeTarget.mAttrs.type == TYPE_BASE_APPLICATION
+ ? activity.getSurfaceControl()
+ : mImeTarget.getSurfaceControl();
+ final SurfaceControl imeSurface = mSurfaceBuilder
+ .setName("IME-snapshot-surface")
+ .setBLASTLayer()
+ .setFormat(buffer.getFormat())
+ // Attaching IME snapshot to the associated IME layering target on the
+ // activity when:
+ // - The target is activity main window: attaching on top of the activity.
+ // - The target is non-activity main window (e.g. activity overlay or
+ // dialog-themed activity): attaching on top of the target since the layer has
+ // already above the activity.
+ .setParent(imeParent)
+ .setCallsite("DisplayContent.attachAndShowImeScreenshotOnTarget")
+ .build();
+ // Make IME snapshot as trusted overlay
+ InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, imeWindow.getDisplayId(),
+ "IME-snapshot-surface");
+ t.setBuffer(imeSurface, buffer);
+ t.setColorSpace(activity.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
+ t.setLayer(imeSurface, 1);
+
+ final Point surfacePosition = new Point(
+ imeWindow.getFrame().left - mImeTarget.getFrame().left,
+ imeWindow.getFrame().top - mImeTarget.getFrame().top);
+ if (imeParent == activity.getSurfaceControl()) {
+ t.setPosition(imeSurface, surfacePosition.x, surfacePosition.y);
+ } else {
+ surfacePosition.offset(mImeTarget.mAttrs.surfaceInsets.left,
+ mImeTarget.mAttrs.surfaceInsets.top);
+ t.setPosition(imeSurface, surfacePosition.x, surfacePosition.y);
+ }
+ return imeSurface;
+ }
+
+ private void removeImeSurface(Transaction t) {
+ if (mImeSurface != null) {
+ if (DEBUG_INPUT_METHOD) Slog.d(TAG, "remove IME snapshot");
+ t.remove(mImeSurface);
+ mImeSurface = null;
+ }
+ }
+
+ void attachAndShow(Transaction t) {
+ final DisplayContent dc = mImeTarget.getDisplayContent();
+ // Prepare IME screenshot for the target if it allows to attach into.
+ final Task task = mImeTarget.getTask();
// Re-new the IME screenshot when it does not exist or the size changed.
- final boolean renewImeSurface = mImeScreenshot == null
- || mImeScreenshot.getWidth() != mInputMethodWindow.getFrame().width()
- || mImeScreenshot.getHeight() != mInputMethodWindow.getFrame().height();
+ final boolean renewImeSurface = mImeSurface == null
+ || mImeSurface.getWidth() != dc.mInputMethodWindow.getFrame().width()
+ || mImeSurface.getHeight() != dc.mInputMethodWindow.getFrame().height();
if (task != null && !task.isActivityTypeHomeOrRecents()) {
SurfaceControl.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
- ? mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
+ ? dc.mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
: null;
if (imeBuffer != null) {
// Remove the last IME surface when the surface needs to renew.
- removeImeSurfaceImmediately();
- mImeScreenshot = createImeSurface(imeBuffer, t);
+ removeImeSurface(t);
+ mImeSurface = createImeSurface(imeBuffer, t);
+ }
+ }
+ final boolean isValidSnapshot = mImeSurface != null && mImeSurface.isValid();
+ // Showing the IME screenshot if the target has already in app transition stage.
+ // Note that if the current IME insets is not showing, no need to show IME screenshot
+ // to reflect the true IME insets visibility and the app task layout as possible.
+ if (isValidSnapshot
+ && dc.getInsetsStateController().getImeSourceProvider().isImeShowing()) {
+ if (DEBUG_INPUT_METHOD) {
+ Slog.d(TAG, "show IME snapshot, ime target=" + mImeTarget);
}
+ t.show(mImeSurface);
+ } else if (!isValidSnapshot) {
+ removeImeSurface(t);
}
}
- final boolean isValidSnapshot = mImeScreenshot != null && mImeScreenshot.isValid();
- // Showing the IME screenshot if the target has already in app transition stage.
- // Note that if the current IME insets is not showing, no need to show IME screenshot
- // to reflect the true IME insets visibility and the app task layout as possible.
- if (isValidSnapshot && getInsetsStateController().getImeSourceProvider().isImeShowing()) {
- if (DEBUG_INPUT_METHOD) {
- Slog.d(TAG, "show IME snapshot, ime target=" + mImeLayeringTarget);
- }
- t.show(mImeScreenshot);
- } else if (!isValidSnapshot) {
- removeImeSurfaceImmediately();
+ void detach(Transaction t) {
+ removeImeSurface(t);
}
}
- @VisibleForTesting
- SurfaceControl createImeSurface(SurfaceControl.ScreenshotHardwareBuffer imeBuffer,
- Transaction t) {
- final HardwareBuffer buffer = imeBuffer.getHardwareBuffer();
- if (DEBUG_INPUT_METHOD) Slog.d(TAG, "create IME snapshot for "
- + mImeLayeringTarget + ", buff width=" + buffer.getWidth()
- + ", height=" + buffer.getHeight());
- final ActivityRecord activity = mImeLayeringTarget.mActivityRecord;
- final SurfaceControl imeSurface = mWmService.mSurfaceControlFactory.apply(null)
- .setName("IME-snapshot-surface")
- .setBLASTLayer()
- .setFormat(buffer.getFormat())
- .setParent(activity.getSurfaceControl())
- .setCallsite("DisplayContent.attachAndShowImeScreenshotOnTarget")
- .build();
- // Make IME snapshot as trusted overlay
- InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, getDisplayId(),
- "IME-snapshot-surface");
- t.setBuffer(imeSurface, buffer);
- t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
- t.setLayer(imeSurface, 1);
- t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left,
- mInputMethodWindow.getDisplayFrame().top);
- return imeSurface;
+ private void attachAndShowImeScreenshotOnTarget() {
+ // No need to attach screenshot if the IME target not exists or screen is off.
+ if (!shouldImeAttachedToApp() || !mWmService.mPolicy.isScreenOn()) {
+ return;
+ }
+
+ final SurfaceControl.Transaction t = getPendingTransaction();
+ // Prepare IME screenshot for the target if it allows to attach into.
+ if (mInputMethodWindow != null && mInputMethodWindow.isVisible()) {
+ mImeScreenshot = new ImeScreenshot(
+ mWmService.mSurfaceControlFactory.apply(null), mImeLayeringTarget);
+ mImeScreenshot.attachAndShow(t);
+ }
}
/**
@@ -4111,8 +4170,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void removeImeScreenshotIfPossible() {
if (mImeLayeringTarget == null
|| mImeLayeringTarget.mAttrs.type != TYPE_APPLICATION_STARTING
- && !mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ && !mImeLayeringTarget.inAppOrRecentsTransition()) {
removeImeSurfaceImmediately();
}
}
@@ -4120,11 +4178,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Removes the IME screenshot immediately. */
void removeImeSurfaceImmediately() {
if (mImeScreenshot != null) {
- if (DEBUG_INPUT_METHOD) Slog.d(TAG, "remove IME snapshot");
- getSyncTransaction().remove(mImeScreenshot);
+ mImeScreenshot.detach(getSyncTransaction());
mImeScreenshot = null;
}
}
+ // ========== End of ImeScreenshot stuff ==========
/**
* The IME input target is the window which receives input from IME. It is also a candidate
@@ -4433,7 +4491,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* hierarchy.
*/
void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
- if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) {
+ if (mImeScreenshot != null && (wc == mImeScreenshot.getImeTarget()
+ || wc.getWindow(w -> w == mImeScreenshot.getImeTarget()) != null)
+ && (type & WindowState.EXIT_ANIMATING_TYPES) != 0) {
removeImeSurfaceImmediately();
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 00380bb6e84e..c47d778c60bc 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -44,6 +44,7 @@ import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -127,7 +128,7 @@ class DragState {
@Nullable private ValueAnimator mAnimator;
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
- private Point mDisplaySize = new Point();
+ private final Point mDisplaySize = new Point();
// A surface used to catch input events for the drag-and-drop operation.
SurfaceControl mInputSurface;
@@ -160,8 +161,7 @@ class DragState {
private void showInputSurface() {
if (mInputSurface == null) {
- mInputSurface = mService.makeSurfaceBuilder(
- mService.mRoot.getDisplayContent(mDisplayContent.getDisplayId()).getSession())
+ mInputSurface = mService.makeSurfaceBuilder(mDisplayContent.getSession())
.setContainerLayer()
.setName("Drag and Drop Input Consumer")
.setCallsite("DragState.showInputSurface")
@@ -174,17 +174,18 @@ class DragState {
return;
}
- mTransaction.show(mInputSurface);
- mTransaction.setInputWindowInfo(mInputSurface, h);
- mTransaction.setLayer(mInputSurface, Integer.MAX_VALUE);
-
+ // Crop the input surface to the display size.
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
- mTransaction.setWindowCrop(mInputSurface, mTmpClipRect);
+
+ mTransaction.show(mInputSurface)
+ .setInputWindowInfo(mInputSurface, h)
+ .setLayer(mInputSurface, Integer.MAX_VALUE)
+ .setCrop(mInputSurface, mTmpClipRect);
// syncInputWindows here to ensure the input window info is sent before the
// transferTouchFocus is called.
- mTransaction.syncInputWindows();
- mTransaction.apply(true);
+ mTransaction.syncInputWindows()
+ .apply(true /*sync*/);
}
/**
@@ -361,29 +362,20 @@ class DragState {
display.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.token = mClientChannel.getToken();
- mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mDragWindowHandle.visible = true;
- // Allows the system to consume keys when dragging is active. This can also be used to
- // modify the drag state on key press. Example, cancel drag on escape key.
- mDragWindowHandle.focusable = true;
- mDragWindowHandle.hasWallpaper = false;
- mDragWindowHandle.paused = false;
mDragWindowHandle.ownerPid = Process.myPid();
mDragWindowHandle.ownerUid = Process.myUid();
- mDragWindowHandle.inputFeatures = 0;
mDragWindowHandle.scaleFactor = 1.0f;
+ // Keep the default behavior of this window to be focusable, which allows the system
+ // to consume keys when dragging is active. This can also be used to modify the drag
+ // state on key press. For example, cancel drag on escape key.
+ mDragWindowHandle.inputConfig = InputConfig.PREVENT_SPLITTING;
+
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
- // The drag window covers the entire display
- mDragWindowHandle.frameLeft = 0;
- mDragWindowHandle.frameTop = 0;
- mDragWindowHandle.frameRight = mDisplaySize.x;
- mDragWindowHandle.frameBottom = mDisplaySize.y;
-
// Pause rotations before a drag.
ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during drag");
mService.mRoot.forAllDisplays(dc -> {
diff --git a/services/core/java/com/android/server/wm/InputConfigAdapter.java b/services/core/java/com/android/server/wm/InputConfigAdapter.java
new file mode 100644
index 000000000000..2dcde4ee2ba8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InputConfigAdapter.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.InputConfig;
+import android.view.InputWindowHandle.InputConfigFlags;
+import android.view.WindowManager.LayoutParams;
+
+import java.util.List;
+
+/**
+ * A helper to determine the {@link InputConfigFlags} that control the behavior of an input window
+ * from several WM attributes.
+ */
+class InputConfigAdapter {
+ private InputConfigAdapter() {}
+
+ /** Describes a mapping from a flag value to a {@link InputConfigFlags} value. */
+ private static class FlagMapping {
+ final int mFlag;
+ final int mInputConfig;
+ final boolean mInverted;
+
+ FlagMapping(int flag, int inputConfig, boolean inverted) {
+ mFlag = flag;
+ mInputConfig = inputConfig;
+ mInverted = inverted;
+ }
+ }
+
+ /**
+ * A mapping from {@link LayoutParams.InputFeatureFlags} to {@link InputConfigFlags} for
+ * input configurations that can be mapped directly from a corresponding LayoutParams input
+ * feature.
+ */
+ private static final List<FlagMapping> INPUT_FEATURE_TO_CONFIG_MAP = List.of(
+ new FlagMapping(
+ LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL,
+ InputConfig.NO_INPUT_CHANNEL, false /* inverted */),
+ new FlagMapping(
+ LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY,
+ InputConfig.DISABLE_USER_ACTIVITY, false /* inverted */),
+ new FlagMapping(
+ LayoutParams.INPUT_FEATURE_SPY,
+ InputConfig.SPY, false /* inverted */));
+
+ @InputConfigFlags
+ private static final int INPUT_FEATURE_TO_CONFIG_MASK =
+ computeMask(INPUT_FEATURE_TO_CONFIG_MAP);
+
+ /**
+ * A mapping from {@link LayoutParams.Flags} to {@link InputConfigFlags} for input
+ * configurations that can be mapped directly from a corresponding LayoutParams flag.
+ *
+ * NOTE: The layout params flag {@link LayoutParams#FLAG_NOT_FOCUSABLE} is not handled by this
+ * adapter, and must be handled explicitly.
+ */
+ private static final List<FlagMapping> LAYOUT_PARAM_FLAG_TO_CONFIG_MAP = List.of(
+ new FlagMapping(
+ LayoutParams.FLAG_NOT_TOUCHABLE,
+ InputConfig.NOT_TOUCHABLE, false /* inverted */),
+ new FlagMapping(
+ LayoutParams.FLAG_SPLIT_TOUCH,
+ InputConfig.PREVENT_SPLITTING, true /* inverted */),
+ new FlagMapping(
+ LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ InputConfig.WATCH_OUTSIDE_TOUCH, false /* inverted */),
+ new FlagMapping(
+ LayoutParams.FLAG_SLIPPERY,
+ InputConfig.SLIPPERY, false /* inverted */));
+
+ @InputConfigFlags
+ private static final int LAYOUT_PARAM_FLAG_TO_CONFIG_MASK =
+ computeMask(LAYOUT_PARAM_FLAG_TO_CONFIG_MAP);
+
+ /**
+ * Returns a mask of all the input config flags configured by
+ * {@link #getInputConfigFromWindowParams(int, int, int)}.
+ */
+ @InputConfigFlags
+ static int getMask() {
+ return LAYOUT_PARAM_FLAG_TO_CONFIG_MASK | INPUT_FEATURE_TO_CONFIG_MASK
+ | InputConfig.IS_WALLPAPER;
+ }
+
+ /**
+ * Get the {@link InputConfigFlags} value that provides the input window behavior specified by
+ * the given WindowManager attributes.
+ *
+ * Use {@link #getMask()} to get the mask of all the input config flags set by this method.
+ *
+ * @param type the window type
+ * @param flags the window flags
+ * @param inputFeatures the input feature flags
+ */
+ @InputConfigFlags
+ static int getInputConfigFromWindowParams(@LayoutParams.WindowType int type,
+ @LayoutParams.Flags int flags, @LayoutParams.InputFeatureFlags int inputFeatures) {
+ return (type == LayoutParams.TYPE_WALLPAPER ? InputConfig.IS_WALLPAPER : 0)
+ | applyMapping(flags, LAYOUT_PARAM_FLAG_TO_CONFIG_MAP)
+ | applyMapping(inputFeatures, INPUT_FEATURE_TO_CONFIG_MAP);
+ }
+
+ @InputConfigFlags
+ private static int applyMapping(int flags, List<FlagMapping> flagToConfigMap) {
+ int inputConfig = 0;
+ for (final FlagMapping mapping : flagToConfigMap) {
+ final boolean flagSet = (flags & mapping.mFlag) != 0;
+ if (flagSet != mapping.mInverted) {
+ inputConfig |= mapping.mInputConfig;
+ }
+ }
+ return inputConfig;
+ }
+
+ @InputConfigFlags
+ private static int computeMask(List<FlagMapping> flagToConfigMap) {
+ int mask = 0;
+ for (final FlagMapping mapping : flagToConfigMap) {
+ mask |= mapping.mInputConfig;
+ }
+ return mask;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index cbd5646968d7..59be3e05f2c0 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -22,6 +22,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -70,19 +71,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mWindowHandle.name = name;
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
- mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.visible = true;
- mWindowHandle.focusable = false;
- mWindowHandle.hasWallpaper = false;
- mWindowHandle.paused = false;
mWindowHandle.ownerPid = Process.myPid();
mWindowHandle.ownerUid = Process.myUid();
- mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.trustedOverlay = true;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
- mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession())
+ mInputSurface = mService.makeSurfaceBuilder(
+ mService.mRoot.getDisplayContent(displayId).getSession())
.setContainerLayer()
.setName("Input Consumer " + name)
.setCallsite("InputConsumerImpl")
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c4f685b69061..8038f4404799 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -21,7 +21,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
@@ -53,6 +52,7 @@ import android.annotation.Nullable;
import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -61,6 +61,7 @@ import android.util.Slog;
import android.view.InputChannel;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -221,15 +222,13 @@ final class InputMonitor {
inputChannel, clientPid, clientUser, mDisplayId);
switch (name) {
case INPUT_CONSUMER_WALLPAPER:
- consumer.mWindowHandle.hasWallpaper = true;
+ consumer.mWindowHandle.inputConfig |= InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER;
break;
case INPUT_CONSUMER_PIP:
- // The touchable region of the Pip input window is cropped to the bounds of the
- // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
- consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
+ // This is a valid consumer type, but we don't need any additional configurations.
break;
case INPUT_CONSUMER_RECENTS_ANIMATION:
- consumer.mWindowHandle.focusable = true;
+ consumer.mWindowHandle.inputConfig &= ~InputConfig.NOT_FOCUSABLE;
break;
default:
throw new IllegalArgumentException("Illegal input consumer : " + name
@@ -247,11 +246,24 @@ final class InputMonitor {
inputWindowHandle.setToken(w.mInputChannelToken);
inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
- inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures);
inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
- inputWindowHandle.setVisible(w.isVisible());
inputWindowHandle.setWindowToken(w.mClient);
+ // Update layout params flags to force the window to be not touch modal. We do this to
+ // restrict the window's touchable region to the task even if it requests touches outside
+ // its window bounds. An example is a dialog in primary split should get touches outside its
+ // window within the primary task but should not get any touches going to the secondary
+ // task.
+ int flags = w.mAttrs.flags;
+ if (w.mAttrs.isModal()) {
+ flags = flags | FLAG_NOT_TOUCH_MODAL;
+ }
+ inputWindowHandle.setLayoutParamsFlags(flags);
+ inputWindowHandle.setInputConfigMasked(
+ InputConfigAdapter.getInputConfigFromWindowParams(
+ w.mAttrs.type, flags, w.mAttrs.inputFeatures),
+ InputConfigAdapter.getMask());
+
final boolean focusable = w.canReceiveKeys()
&& (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
inputWindowHandle.setFocusable(focusable);
@@ -270,17 +282,6 @@ final class InputMonitor {
// what is on screen to what is actually being touched in the UI.
inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
- // Update layout params flags to force the window to be not touch modal. We do this to
- // restrict the window's touchable region to the task even if it request touches outside its
- // window bounds. An example is a dialog in primary split should get touches outside its
- // window within the primary task but should not get any touches going to the secondary
- // task.
- int flags = w.mAttrs.flags;
- if (w.mAttrs.isModal()) {
- flags = flags | FLAG_NOT_TOUCH_MODAL;
- }
- inputWindowHandle.setLayoutParamsFlags(flags);
-
boolean useSurfaceBoundsAsTouchRegion = false;
SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
@@ -590,6 +591,8 @@ final class InputMonitor {
if (mAddWallpaperInputConsumerHandle) {
if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisible()) {
+ mWallpaperInputConsumer.mWindowHandle
+ .replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
// Add the wallpaper input consumer above the first visible wallpaper.
mWallpaperInputConsumer.show(mInputTransaction, w);
mAddWallpaperInputConsumerHandle = false;
@@ -631,24 +634,27 @@ final class InputMonitor {
static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle,
WindowState w) {
- populateOverlayInputInfo(inputWindowHandle, w.isVisible());
+ populateOverlayInputInfo(inputWindowHandle);
inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
}
// This would reset InputWindowHandle fields to prevent it could be found by input event.
// We need to check if any new field of InputWindowHandle could impact the result.
@VisibleForTesting
- static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle,
- boolean isVisible) {
+ static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle) {
inputWindowHandle.setDispatchingTimeoutMillis(0); // It should never receive input.
- inputWindowHandle.setVisible(isVisible);
inputWindowHandle.setFocusable(false);
- inputWindowHandle.setInputFeatures(INPUT_FEATURE_NO_INPUT_CHANNEL);
// The input window handle without input channel must not have a token.
inputWindowHandle.setToken(null);
inputWindowHandle.setScaleFactor(1f);
- inputWindowHandle.setLayoutParamsFlags(
- FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
+ final int defaultType = WindowManager.LayoutParams.TYPE_APPLICATION;
+ inputWindowHandle.setLayoutParamsType(defaultType);
+ inputWindowHandle.setInputConfigMasked(
+ InputConfigAdapter.getInputConfigFromWindowParams(
+ defaultType,
+ FLAG_NOT_TOUCHABLE,
+ INPUT_FEATURE_NO_INPUT_CHANNEL),
+ InputConfigAdapter.getMask());
inputWindowHandle.clearTouchableRegion();
inputWindowHandle.setTouchableRegionCrop(null);
}
@@ -666,7 +672,7 @@ final class InputMonitor {
inputWindowHandle.setName(name);
inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
inputWindowHandle.setTrustedOverlay(true);
- populateOverlayInputInfo(inputWindowHandle, true /* isVisible */);
+ populateOverlayInputInfo(inputWindowHandle);
setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 142d2933f046..301c1846249f 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -20,10 +20,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
+import android.os.InputConfig;
import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
+import android.view.InputWindowHandle.InputConfigFlags;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import java.util.Objects;
@@ -31,8 +34,10 @@ import java.util.Objects;
* The wrapper of {@link InputWindowHandle} with field change detection to reduce unnecessary
* updates to surface, e.g. if there are no changes, then skip invocation of
* {@link SurfaceControl.Transaction#setInputWindowInfo(SurfaceControl, InputWindowHandle)}.
+ * It also sets the {@link InputConfigFlags} values for the input window.
*/
class InputWindowHandleWrapper {
+
/** The wrapped handle should not be directly exposed to avoid untracked changes. */
private final @NonNull InputWindowHandle mHandle;
@@ -65,7 +70,20 @@ class InputWindowHandleWrapper {
}
boolean isFocusable() {
- return mHandle.focusable;
+ return (mHandle.inputConfig & InputConfig.NOT_FOCUSABLE) == 0;
+ }
+
+ boolean isPaused() {
+ return (mHandle.inputConfig & InputConfig.PAUSE_DISPATCHING) != 0;
+ }
+
+ boolean isTrustedOverlay() {
+ return (mHandle.inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
+ }
+
+ boolean hasWallpaper() {
+ return (mHandle.inputConfig & InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER)
+ != 0;
}
InputApplicationHandle getInputApplicationHandle() {
@@ -96,7 +114,7 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setLayoutParamsFlags(int flags) {
+ void setLayoutParamsFlags(@WindowManager.LayoutParams.Flags int flags) {
if (mHandle.layoutParamsFlags == flags) {
return;
}
@@ -136,19 +154,11 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setVisible(boolean visible) {
- if (mHandle.visible == visible) {
- return;
- }
- mHandle.visible = visible;
- mChanged = true;
- }
-
void setFocusable(boolean focusable) {
- if (mHandle.focusable == focusable) {
+ if (isFocusable() == focusable) {
return;
}
- mHandle.focusable = focusable;
+ mHandle.setInputConfig(InputConfig.NOT_FOCUSABLE, !focusable);
mChanged = true;
}
@@ -161,26 +171,27 @@ class InputWindowHandleWrapper {
}
void setHasWallpaper(boolean hasWallpaper) {
- if (mHandle.hasWallpaper == hasWallpaper) {
+ if (this.hasWallpaper() == hasWallpaper) {
return;
}
- mHandle.hasWallpaper = hasWallpaper;
+ mHandle.setInputConfig(InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
+ hasWallpaper);
mChanged = true;
}
void setPaused(boolean paused) {
- if (mHandle.paused == paused) {
+ if (isPaused() == paused) {
return;
}
- mHandle.paused = paused;
+ mHandle.setInputConfig(InputConfig.PAUSE_DISPATCHING, paused);
mChanged = true;
}
void setTrustedOverlay(boolean trustedOverlay) {
- if (mHandle.trustedOverlay == trustedOverlay) {
+ if (isTrustedOverlay() == trustedOverlay) {
return;
}
- mHandle.trustedOverlay = trustedOverlay;
+ mHandle.setInputConfig(InputConfig.TRUSTED_OVERLAY, trustedOverlay);
mChanged = true;
}
@@ -208,14 +219,6 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setInputFeatures(int features) {
- if (mHandle.inputFeatures == features) {
- return;
- }
- mHandle.inputFeatures = features;
- mChanged = true;
- }
-
void setDisplayId(int displayId) {
if (mHandle.displayId == displayId) {
return;
@@ -276,8 +279,14 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- boolean isTrustedOverlay() {
- return mHandle.trustedOverlay;
+ void setInputConfigMasked(@InputConfigFlags int inputConfig, @InputConfigFlags int mask) {
+ final int inputConfigMasked = inputConfig & mask;
+ if (inputConfigMasked == (mHandle.inputConfig & mask)) {
+ return;
+ }
+ mHandle.inputConfig &= ~mask;
+ mHandle.inputConfig |= inputConfigMasked;
+ mChanged = true;
}
@Override
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 4b98013a99cc..40df02c176e5 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -24,6 +24,7 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Process;
import android.view.GestureDetector;
import android.view.InputChannel;
@@ -290,16 +291,12 @@ public class Letterbox {
win.getDisplayId());
mWindowHandle.name = name;
mWindowHandle.token = mToken;
- mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_SLIPPERY;
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.visible = true;
mWindowHandle.ownerPid = Process.myPid();
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.scaleFactor = 1.0f;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SLIPPERY;
}
void updateTouchableRegion(Rect frame) {
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 7bddb620c94d..f3713eb7f474 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -19,20 +19,46 @@ package com.android.server.wm;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-import android.util.ArraySet;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.view.Display;
import android.view.Display.Mode;
import android.view.DisplayInfo;
+import java.util.HashMap;
+
/**
* Policy to select a lower refresh rate for the display if applicable.
*/
class RefreshRatePolicy {
+ class PackageRefreshRate {
+ private final HashMap<String, RefreshRateRange> mPackages = new HashMap<>();
+
+ public void add(String s, float minRefreshRate, float maxRefreshRate) {
+ float minSupportedRefreshRate =
+ Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate);
+ float maxSupportedRefreshRate =
+ Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate);
+
+ mPackages.put(s,
+ new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate));
+ }
+
+ public RefreshRateRange get(String s) {
+ return mPackages.get(s);
+ }
+
+ public void remove(String s) {
+ mPackages.remove(s);
+ }
+ }
+
private final Mode mLowRefreshRateMode;
- private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
+ private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
private final WindowManagerService mWmService;
+ private float mMinSupportedRefreshRate;
+ private float mMaxSupportedRefreshRate;
/**
* The following constants represent priority of the window. SF uses this information when
@@ -70,7 +96,12 @@ class RefreshRatePolicy {
Mode mode = displayInfo.getDefaultMode();
float[] refreshRates = displayInfo.getDefaultRefreshRates();
float bestRefreshRate = mode.getRefreshRate();
+ mMinSupportedRefreshRate = bestRefreshRate;
+ mMaxSupportedRefreshRate = bestRefreshRate;
for (int i = refreshRates.length - 1; i >= 0; i--) {
+ mMinSupportedRefreshRate = Math.min(mMinSupportedRefreshRate, refreshRates[i]);
+ mMaxSupportedRefreshRate = Math.max(mMaxSupportedRefreshRate, refreshRates[i]);
+
if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) {
bestRefreshRate = refreshRates[i];
}
@@ -78,12 +109,13 @@ class RefreshRatePolicy {
return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate);
}
- void addNonHighRefreshRatePackage(String packageName) {
- mNonHighRefreshRatePackages.add(packageName);
+ void addRefreshRateRangeForPackage(String packageName,
+ float minRefreshRate, float maxRefreshRate) {
+ mNonHighRefreshRatePackages.add(packageName, minRefreshRate, maxRefreshRate);
mWmService.requestTraversal();
}
- void removeNonHighRefreshRatePackage(String packageName) {
+ void removeRefreshRateRangeForPackage(String packageName) {
mNonHighRefreshRatePackages.remove(packageName);
mWmService.requestTraversal();
}
@@ -172,8 +204,9 @@ class RefreshRatePolicy {
// If app is using Camera, we set both the min and max refresh rate to the camera's
// preferred refresh rate to make sure we don't end up with a refresh rate lower
// than the camera capture rate, which will lead to dropping camera frames.
- if (mNonHighRefreshRatePackages.contains(packageName)) {
- return mLowRefreshRateMode.getRefreshRate();
+ RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
+ if (range != null) {
+ return range.min;
}
return 0;
@@ -192,8 +225,9 @@ class RefreshRatePolicy {
final String packageName = w.getOwningPackage();
// If app is using Camera, force it to default (lower) refresh rate.
- if (mNonHighRefreshRatePackages.contains(packageName)) {
- return mLowRefreshRateMode.getRefreshRate();
+ RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName);
+ if (range != null) {
+ return range.max;
}
return 0;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b9cd657da0e2..235533341433 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -149,6 +149,7 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import java.io.FileDescriptor;
@@ -2727,9 +2728,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return false;
}
- Task getLaunchRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable Task candidateTask, boolean onTop) {
- return getLaunchRootTask(r, options, candidateTask, null /* sourceTask */, onTop,
+ return getOrCreateRootTask(r, options, candidateTask, null /* sourceTask */, onTop,
null /* launchParams */, 0 /* launchFlags */);
}
@@ -2744,63 +2745,68 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @param launchFlags The launch flags for this launch.
* @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
* @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
- * @return The root task to use for the launch or INVALID_TASK_ID.
+ * @return The root task to use for the launch.
*/
- Task getLaunchRootTask(@Nullable ActivityRecord r,
+ Task getOrCreateRootTask(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask,
@Nullable Task sourceTask, boolean onTop,
@Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
- int taskId = INVALID_TASK_ID;
- int displayId = INVALID_DISPLAY;
- TaskDisplayArea taskDisplayArea = null;
-
- // We give preference to the launch preference in activity options.
+ // First preference goes to the launch root task set in the activity options.
if (options != null) {
- taskId = options.getLaunchTaskId();
- displayId = options.getLaunchDisplayId();
- final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
- taskDisplayArea = daToken != null
- ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
-
- final Task rootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
- if (rootTask != null) {
- return rootTask;
+ final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());
+ if (canLaunchOnDisplay(r, candidateRoot)) {
+ return candidateRoot;
}
}
- // First preference for root task goes to the task Id set in the activity options. Use
- // the root task associated with that if possible.
- if (taskId != INVALID_TASK_ID) {
- // Temporarily set the task id to invalid in case in re-entry.
- options.setLaunchTaskId(INVALID_TASK_ID);
- final Task task = anyTaskForId(taskId,
- MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
- options.setLaunchTaskId(taskId);
- if (task != null) {
- return task.getRootTask();
+ // Next preference goes to the task id set in the activity options.
+ if (options != null) {
+ final int candidateTaskId = options.getLaunchTaskId();
+ if (candidateTaskId != INVALID_TASK_ID) {
+ // Temporarily set the task id to invalid in case in re-entry.
+ options.setLaunchTaskId(INVALID_TASK_ID);
+ final Task task = anyTaskForId(candidateTaskId,
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+ options.setLaunchTaskId(candidateTaskId);
+ if (canLaunchOnDisplay(r, task)) {
+ return task.getRootTask();
+ }
}
}
- final int activityType = resolveActivityType(r, options, candidateTask);
- Task rootTask = null;
-
- // Next preference for root task goes to the taskDisplayArea candidate.
- if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null
- && canLaunchOnDisplay(r, launchParams.mPreferredTaskDisplayArea.getDisplayId())) {
+ // Next preference goes to the TaskDisplayArea candidate from launchParams
+ // or activity options.
+ TaskDisplayArea taskDisplayArea = null;
+ if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
+ } else if (options != null) {
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ taskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+ if (taskDisplayArea == null) {
+ final int launchDisplayId = options.getLaunchDisplayId();
+ if (launchDisplayId != INVALID_DISPLAY) {
+ final DisplayContent displayContent = getDisplayContent(launchDisplayId);
+ if (displayContent != null) {
+ taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
+ }
+ }
+ }
}
- if (taskDisplayArea == null && displayId != INVALID_DISPLAY
- && canLaunchOnDisplay(r, displayId)) {
- taskDisplayArea = getDisplayContent(displayId).getDefaultTaskDisplayArea();
- }
+
+ final int activityType = resolveActivityType(r, options, candidateTask);
if (taskDisplayArea != null) {
- return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
- sourceTask, launchParams, launchFlags, activityType, onTop);
+ if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
+ return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
+ sourceTask, launchParams, launchFlags, activityType, onTop);
+ } else {
+ taskDisplayArea = null;
+ }
}
// Give preference to the root task and display of the input task and activity if they
// match the mode we want to launch into.
- TaskDisplayArea container = null;
+ Task rootTask = null;
if (candidateTask != null) {
rootTask = candidateTask.getRootTask();
}
@@ -2810,10 +2816,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
int windowingMode = launchParams != null ? launchParams.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (rootTask != null) {
- container = rootTask.getDisplayArea();
- if (container != null && canLaunchOnDisplay(r, container.mDisplayContent.mDisplayId)) {
+ taskDisplayArea = rootTask.getDisplayArea();
+ if (taskDisplayArea != null
+ && canLaunchOnDisplay(r, taskDisplayArea.mDisplayContent.mDisplayId)) {
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = container.resolveWindowingMode(r, options, candidateTask);
+ windowingMode = taskDisplayArea.resolveWindowingMode(r, options, candidateTask);
}
// Always allow organized tasks that created by organizer since the activity type
// of an organized task is decided by the activity type of its top child, which
@@ -2822,19 +2829,32 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|| rootTask.mCreatedByOrganizer) {
return rootTask;
}
+ } else {
+ taskDisplayArea = null;
}
+
}
- if (container == null
- || !canLaunchOnDisplay(r, container.mDisplayContent.mDisplayId)) {
- container = getDefaultTaskDisplayArea();
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = container.resolveWindowingMode(r, options, candidateTask);
- }
+ // Falling back to default task container
+ if (taskDisplayArea == null) {
+ taskDisplayArea = getDefaultTaskDisplayArea();
+ }
+ return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
+ launchParams, launchFlags, activityType, onTop);
+ }
+
+ private boolean canLaunchOnDisplay(ActivityRecord r, Task task) {
+ if (task == null) {
+ Slog.w(TAG, "canLaunchOnDisplay(), invalid task: " + task);
+ return false;
+ }
+
+ if (!task.isAttached()) {
+ Slog.w(TAG, "canLaunchOnDisplay(), Task is not attached: " + task);
+ return false;
}
- return container.getOrCreateRootTask(r, options, candidateTask, sourceTask, launchParams,
- launchFlags, activityType, onTop);
+ return canLaunchOnDisplay(r, task.getTaskDisplayArea().getDisplayId());
}
/** @return true if activity record is null or can be launched on provided display. */
@@ -2842,7 +2862,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (r == null) {
return true;
}
- return r.canBeLaunchedOnDisplay(displayId);
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ Slog.w(TAG, "Not allow to launch " + r + " on display " + displayId);
+ return false;
+ }
+ return true;
}
int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@@ -3212,12 +3236,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (aOptions != null) {
// Resolve the root task the task should be placed in now based on options
// and reparent if needed.
- final Task launchRootTask =
- getLaunchRootTask(null, aOptions, task, onTop);
- if (launchRootTask != null && task.getRootTask() != launchRootTask) {
+ final Task targetRootTask =
+ getOrCreateRootTask(null, aOptions, task, onTop);
+ if (targetRootTask != null && task.getRootTask() != targetRootTask) {
final int reparentMode = onTop
? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
- task.reparent(launchRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+ task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
"anyTaskForId");
}
}
@@ -3309,6 +3333,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
/**
+ * Iterate over all task fragments, to see if there exists one that meets the
+ * PermissionPolicyService's criteria to show a permission dialog.
+ */
+ public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
+ PermissionPolicyInternal pPi = mService.getPermissionPolicyInternal();
+ if (pPi == null) {
+ return INVALID_TASK_ID;
+ }
+
+ final int[] validTaskId = {INVALID_TASK_ID};
+ forAllLeafTaskFragments(fragment -> {
+ ActivityRecord record = fragment.getActivity((r) -> {
+ // skip hidden (or about to hide) apps, or the permission dialog
+ return r.canBeTopRunning() && r.isVisibleRequested()
+ && !pPi.isIntentToPermissionDialog(r.intent);
+ });
+ if (record != null && record.isUid(uid)
+ && Objects.equals(pkgName, record.packageName)
+ && pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(),
+ pkgName, record.intent)) {
+ validTaskId[0] = record.getTask().mTaskId;
+ return true;
+ }
+ return false;
+ });
+
+ return validTaskId[0];
+ }
+
+ /**
* Dumps the activities matching the given {@param name} in the either the focused root task
* or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true.
*/
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 3d6d1825adc7..1ec191ed7c05 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -57,8 +57,6 @@ class RunningTasks {
private ArraySet<Integer> mProfileIds;
private boolean mAllowed;
private boolean mFilterOnlyVisibleRecents;
- private Task mTopDisplayFocusRootTask;
- private Task mTopDisplayAdjacentTask;
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
@@ -78,17 +76,9 @@ class RunningTasks {
mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
mFilterOnlyVisibleRecents =
(flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
- mTopDisplayFocusRootTask = root.getTopDisplayFocusedRootTask();
mRecentTasks = root.mService.getRecentTasks();
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
- if (mTopDisplayFocusRootTask != null
- && mTopDisplayFocusRootTask.getAdjacentTaskFragment() != null) {
- mTopDisplayAdjacentTask = mTopDisplayFocusRootTask.getAdjacentTaskFragment().asTask();
- } else {
- mTopDisplayAdjacentTask = null;
- }
-
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
PooledLambda.__(Task.class));
root.forAllLeafTasks(c, false);
@@ -132,18 +122,15 @@ class RunningTasks {
return;
}
- final Task rootTask = task.getRootTask();
- if (rootTask == mTopDisplayFocusRootTask && rootTask.getTopMostTask() == task) {
- // For the focused top root task, update the last root task active time so that it
- // can be used to determine the order of the tasks (it may not be set for newly
- // created tasks)
+ if (task.isVisible()) {
+ // For the visible task, update the last active time so that it can be used to determine
+ // the order of the tasks (it may not be set for newly created tasks)
task.touchActiveTime();
- } else if (rootTask == mTopDisplayAdjacentTask && rootTask.getTopMostTask() == task) {
- // The short-term workaround for launcher could get suitable running task info in
- // split screen.
- task.touchActiveTime();
- // TreeSet doesn't allow same value and make sure this task is lower than focus one.
- task.lastActiveTime--;
+ if (!task.isFocused()) {
+ // TreeSet doesn't allow the same value and make sure this task is lower than the
+ // focused one.
+ task.lastActiveTime -= mTmpSortedSet.size();
+ }
}
mTmpSortedSet.add(task);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7fe34f47c53c..b3ae60287534 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -504,13 +504,6 @@ class Task extends TaskFragment {
*/
boolean mInRemoveTask;
- // When non-null, this is a transaction that will get applied on the next frame returned after
- // a relayout is requested from the client. While this is only valid on a leaf task; since the
- // transaction can effect an ancestor task, this also needs to keep track of the ancestor task
- // that this transaction manipulates because deferUntilFrame acts on individual surfaces.
- SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
- Task mMainWindowSizeChangeTask;
-
private final AnimatingActivityRegistry mAnimatingActivityRegistry =
new AnimatingActivityRegistry();
@@ -4287,7 +4280,7 @@ class Task extends TaskFragment {
/**
* @return true if the task is currently focused.
*/
- private boolean isFocused() {
+ boolean isFocused() {
if (mDisplayContent == null || mDisplayContent.mFocusedApp == null) {
return false;
}
@@ -4390,17 +4383,16 @@ class Task extends TaskFragment {
leaf.setMainWindowSizeChangeTransaction(t, origin);
return;
}
- mMainWindowSizeChangeTransaction = t;
- mMainWindowSizeChangeTask = t == null ? null : origin;
- }
-
- SurfaceControl.Transaction getMainWindowSizeChangeTransaction() {
- return mMainWindowSizeChangeTransaction;
+ final WindowState w = getTopVisibleAppMainWindow();
+ if (w != null) {
+ w.applyWithNextDraw((d) -> {
+ d.merge(t);
+ });
+ } else {
+ t.apply();
+ }
}
- Task getMainWindowSizeChangeTask() {
- return mMainWindowSizeChangeTask;
- }
void setActivityWindowingMode(int windowingMode) {
PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index afc3087f4ee9..3411104dbff0 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -516,12 +516,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
*/
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
- if ((a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
- == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING) {
- return true;
- }
+ return isAllowedToEmbedActivityInUntrustedMode(a)
+ || isAllowedToEmbedActivityInTrustedMode(a);
+ }
- return isAllowedToEmbedActivityInTrustedMode(a);
+ boolean isAllowedToEmbedActivityInUntrustedMode(@NonNull ActivityRecord a) {
+ return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
+ == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
}
/**
@@ -531,7 +532,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
* <li>the activity has declared the organizer host as trusted explicitly via known
* certificate.</li>
*/
- private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
+ boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
if (UserHandle.getAppId(mTaskFragmentOrganizerUid) == SYSTEM_UID) {
// The system is trusted to embed other apps securely and for all users.
return true;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 331f1242da1d..ff5bfbee61f4 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -800,17 +800,24 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- DisplayContent dc = mService.mWindowManager.mRoot
+ final DisplayContent dc = mService.mWindowManager.mRoot
.getDisplayContent(displayId);
- if (dc == null || dc.getImeTarget(IME_TARGET_LAYERING) == null) {
+ if (dc == null) {
+ return null;
+ }
+
+ final InsetsControlTarget imeLayeringTarget = dc.getImeTarget(IME_TARGET_LAYERING);
+ if (imeLayeringTarget == null || imeLayeringTarget.getWindow() == null) {
return null;
}
+
// Avoid WindowState#getRootTask() so we don't attribute system windows to a task.
- final Task task = dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getTask();
+ final Task task = imeLayeringTarget.getWindow().asTask();
if (task == null) {
return null;
}
- return task.getRootTask().mRemoteToken.toWindowContainerToken();
+
+ return task.mRemoteToken.toWindowContainerToken();
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 348cfb62582e..47c397d12720 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -40,6 +40,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.Process;
import android.os.RemoteException;
import android.os.Trace;
@@ -219,30 +220,17 @@ class TaskPositioner implements IBinder.DeathRecipient {
displayContent.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.token = mClientChannel.getToken();
- mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mDragWindowHandle.visible = true;
- // When dragging the window around, we do not want to steal focus for the window.
- mDragWindowHandle.focusable = false;
- mDragWindowHandle.hasWallpaper = false;
- mDragWindowHandle.paused = false;
mDragWindowHandle.ownerPid = Process.myPid();
mDragWindowHandle.ownerUid = Process.myUid();
- mDragWindowHandle.inputFeatures = 0;
mDragWindowHandle.scaleFactor = 1.0f;
+ // When dragging the window around, we do not want to steal focus for the window.
+ mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
- // The drag window covers the entire display.
- final Rect displayBounds = mTmpRect;
- displayContent.getBounds(mTmpRect);
- mDragWindowHandle.frameLeft = displayBounds.left;
- mDragWindowHandle.frameTop = displayBounds.top;
- mDragWindowHandle.frameRight = displayBounds.right;
- mDragWindowHandle.frameBottom = displayBounds.bottom;
-
// Pause rotations before a drag.
ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
mDisplayContent.getDisplayRotation().pause();
@@ -250,6 +238,8 @@ class TaskPositioner implements IBinder.DeathRecipient {
// Notify InputMonitor to take mDragWindowHandle.
mService.mTaskPositioningController.showInputSurface(win.getDisplayId());
+ final Rect displayBounds = mTmpRect;
+ displayContent.getBounds(displayBounds);
final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 8f24f2615fac..68bf2b2424e7 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -83,17 +83,18 @@ class TaskPositioningController {
return;
}
- mTransaction.show(mInputSurface);
- mTransaction.setInputWindowInfo(mInputSurface, h);
- mTransaction.setLayer(mInputSurface, Integer.MAX_VALUE);
-
final Display display = dc.getDisplay();
final Point p = new Point();
display.getRealSize(p);
-
mTmpClipRect.set(0, 0, p.x, p.y);
- mTransaction.setWindowCrop(mInputSurface, mTmpClipRect);
- mTransaction.syncInputWindows().apply();
+
+ mTransaction.show(mInputSurface)
+ .setInputWindowInfo(mInputSurface, h)
+ .setLayer(mInputSurface, Integer.MAX_VALUE)
+ .setPosition(mInputSurface, 0, 0)
+ .setCrop(mInputSurface, mTmpClipRect)
+ .syncInputWindows()
+ .apply();
}
boolean startMovingTask(IWindow window, float startX, float startY) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 29d17429fa6a..4c23f3991c58 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -574,6 +574,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
false /* disableImeIcon */);
}
}
+ dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
}
@@ -1526,6 +1527,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (task != null && task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
}
+ if (task != null && task.isTranslucent(null)) {
+ flags |= FLAG_TRANSLUCENT;
+ }
final ActivityRecord record = wc.asActivityRecord();
if (record != null) {
if (record.mUseTransferredAnimation) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 043623392546..8840cd557de6 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -61,7 +61,7 @@ class TransitionController {
/** Whether to use shell-transitions rotation instead of fixed-rotation. */
private static final boolean SHELL_TRANSITIONS_ROTATION =
- SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
/** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
private static final int DEFAULT_TIMEOUT_MS = 5000;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9585a4b93a97..0a3c3f049f43 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -725,17 +725,18 @@ public abstract class WindowManagerInternal {
public abstract void hideIme(IBinder imeTargetWindowToken, int displayId);
/**
- * Tell window manager about a package that should not be running with high refresh rate
- * setting until removeNonHighRefreshRatePackage is called for the same package.
+ * Tell window manager about a package that should be running with a restricted range of
+ * refresh rate setting until removeRefreshRateRangeForPackage is called for the same package.
*
* This must not be called again for the same package.
*/
- public abstract void addNonHighRefreshRatePackage(@NonNull String packageName);
+ public abstract void addRefreshRateRangeForPackage(@NonNull String packageName,
+ float minRefreshRate, float maxRefreshRate);
/**
* Tell window manager to stop constraining refresh rate for the given package.
*/
- public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
+ public abstract void removeRefreshRateRangeForPackage(@NonNull String packageName);
/**
* Checks if the device supports touch or faketouch.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2cff03089234..4e476592e817 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -192,6 +192,7 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.InputConfig;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -405,7 +406,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Use WMShell for app transition.
*/
- public static final String ENABLE_SHELL_TRANSITIONS = "persist.debug.shell_transit";
+ public static final String ENABLE_SHELL_TRANSITIONS = "persist.wm.debug.shell_transit";
/**
* @see #ENABLE_SHELL_TRANSITIONS
@@ -7968,18 +7969,20 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void addNonHighRefreshRatePackage(@NonNull String packageName) {
+ public void addRefreshRateRangeForPackage(@NonNull String packageName,
+ float minRefreshRate, float maxRefreshRate) {
synchronized (mGlobalLock) {
mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
- .addNonHighRefreshRatePackage(packageName));
+ .addRefreshRateRangeForPackage(
+ packageName, minRefreshRate, maxRefreshRate));
}
}
@Override
- public void removeNonHighRefreshRatePackage(@NonNull String packageName) {
+ public void removeRefreshRateRangeForPackage(@NonNull String packageName) {
synchronized (mGlobalLock) {
mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
- .removeNonHighRefreshRatePackage(packageName));
+ .removeRefreshRateRangeForPackage(packageName));
}
}
@@ -8452,43 +8455,48 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
- int displayId, SurfaceControl surface, String name,
- InputApplicationHandle applicationHandle, int flags,
- int privateFlags, int type, Region region, IWindow window) {
- InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
+ int displayId, SurfaceControl surface, String name,
+ InputApplicationHandle applicationHandle, int flags,
+ int privateFlags, int type, Region region, IWindow window) {
+ final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
h.setWindowToken(window);
h.name = name;
flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
- final int sanitizedFlags = flags & (FLAG_NOT_TOUCHABLE
- | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
- h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
+ final int sanitizedLpFlags =
+ (flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE))
+ | LayoutParams.FLAG_NOT_TOUCH_MODAL;
h.layoutParamsType = type;
- h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- h.focusable = (flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0;
- h.hasWallpaper = false;
- h.paused = false;
+ h.layoutParamsFlags = sanitizedLpFlags;
+
+ // Do not allow any input features to be set without sanitizing them first.
+ h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams(
+ type, sanitizedLpFlags, 0 /*inputFeatures*/);
+
+
+ if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ h.inputConfig |= InputConfig.NOT_FOCUSABLE;
+ }
+
+ // Check private trusted overlay flag to set trustedOverlay field of input window handle.
+ if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
+ h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
+ }
+ h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
h.ownerUid = callingUid;
h.ownerPid = callingPid;
- // Do not allow any input features to be set without sanitizing them first.
- h.inputFeatures = 0;
-
if (region == null) {
- h.replaceTouchableRegionWithCrop(null);
+ h.replaceTouchableRegionWithCrop = true;
} else {
h.touchableRegion.set(region);
- h.replaceTouchableRegionWithCrop = false;
- h.setTouchableRegionCrop(surface);
}
+ h.setTouchableRegionCrop(null /* use the input surface's bounds */);
- // Check private trusted overlay flag to set trustedOverlay field of input window handle.
- h.trustedOverlay = (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0;
-
- SurfaceControl.Transaction t = mTransactionFactory.get();
+ final SurfaceControl.Transaction t = mTransactionFactory.get();
t.setInputWindowInfo(surface, h);
t.apply();
t.close();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ce27d739e1b1..81344ac31108 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -701,6 +701,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
errorCallbackToken,
convertStartFailureToThrowable(result, activityIntent));
+ } else {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8ee6feb26a53..db687f627bfa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1190,6 +1190,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mActivityRecord != null
? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
getDisplayId()));
+ mInputWindowHandle.setFocusable(false);
mInputWindowHandle.setOwnerPid(s.mPid);
mInputWindowHandle.setOwnerUid(s.mUid);
mInputWindowHandle.setName(getName());
@@ -5987,15 +5988,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
finishDrawing(null);
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
if (!useBLASTSync()) return;
-
- final Task task = getTask();
- if (task != null) {
- final SurfaceControl.Transaction t = task.getMainWindowSizeChangeTransaction();
- if (t != null) {
- mSyncTransaction.merge(t);
- }
- task.setMainWindowSizeChangeTransaction(null);
- }
}
@Override
@@ -6032,10 +6024,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mRedrawForSyncReported) {
return false;
}
- final Task task = getTask();
- if (task != null && task.getMainWindowSizeChangeTransaction() != null) {
- return true;
- }
return useBLASTSync();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c17961e8a564..285a6d5bdf5f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -442,50 +442,6 @@ class WindowStateAnimator {
return mService.useBLASTSync() && mWin.useBLASTSync();
}
- private boolean shouldConsumeMainWindowSizeTransaction() {
- // We only consume the transaction when the client is calling relayout
- // because this is the only time we know the frameNumber will be valid
- // due to the client renderer being paused. Put otherwise, only when
- // mInRelayout is true can we guarantee the next frame will contain
- // the most recent configuration.
- if (!mWin.mInRelayout) return false;
- // Since we can only do this for one window, we focus on the main application window
- if (mAttrType != TYPE_BASE_APPLICATION) return false;
- final Task task = mWin.getTask();
- if (task == null) return false;
- if (task.getMainWindowSizeChangeTransaction() == null) return false;
- // Likewise we only focus on the task root, since we can only use one window
- if (!mWin.mActivityRecord.isRootOfTask()) return false;
- return true;
- }
-
- void setSurfaceBoundariesLocked(SurfaceControl.Transaction t) {
- if (mSurfaceController == null) {
- return;
- }
-
- final WindowState w = mWin;
- final Task task = w.getTask();
- if (shouldConsumeMainWindowSizeTransaction()) {
- if (isInBlastSync()) {
- // If we're in a sync transaction, there's no need to call defer transaction.
- // The sync transaction will contain the buffer so the bounds change transaction
- // will only be applied with the buffer.
- t.merge(task.getMainWindowSizeChangeTransaction());
- task.setMainWindowSizeChangeTransaction(null);
- } else {
- mWin.applyWithNextDraw(finishedFrame -> {
- final SurfaceControl.Transaction sizeChangedTransaction =
- task.getMainWindowSizeChangeTransaction();
- if (sizeChangedTransaction != null) {
- finishedFrame.merge(sizeChangedTransaction);
- task.setMainWindowSizeChangeTransaction(null);
- }
- });
- }
- }
- }
-
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
if (!hasSurface()) {
@@ -501,8 +457,6 @@ class WindowStateAnimator {
computeShownFrameLocked();
- setSurfaceBoundariesLocked(t);
-
if (w.isParentWindowHidden() || !w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 2090ab367438..2f5ab0b31332 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -307,8 +307,8 @@ class ActiveAdmin {
public boolean mAdminCanGrantSensorsPermissions;
public boolean mPreferentialNetworkServiceEnabled =
DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT;
- public PreferentialNetworkServiceConfig mPreferentialNetworkServiceConfig =
- PreferentialNetworkServiceConfig.DEFAULT;
+ public List<PreferentialNetworkServiceConfig> mPreferentialNetworkServiceConfigs =
+ List.of(PreferentialNetworkServiceConfig.DEFAULT);
private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7178cbd460c0..3d40f48f244d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -87,6 +87,7 @@ import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.admin.DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
import static android.app.admin.DevicePolicyManager.STATUS_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE;
@@ -132,7 +133,6 @@ import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
-import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
@@ -140,6 +140,7 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
+import static android.provider.Telephony.Carriers.INVALID_APN_ID;
import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
@@ -3356,14 +3357,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
- final PreferentialNetworkServiceConfig preferentialNetworkServiceConfig;
+ final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
- preferentialNetworkServiceConfig = owner != null
- ? owner.mPreferentialNetworkServiceConfig
- : PreferentialNetworkServiceConfig.DEFAULT;
+ preferentialNetworkServiceConfigs = owner != null
+ ? owner.mPreferentialNetworkServiceConfigs
+ : List.of(PreferentialNetworkServiceConfig.DEFAULT);
}
- updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfig);
+ updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
startOwnerService(userId, "start-user");
}
@@ -3380,7 +3381,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
void handleStopUser(int userId) {
- updateNetworkPreferenceForUser(userId, PreferentialNetworkServiceConfig.DEFAULT);
+ updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
stopOwnerService(userId, "stop-user");
}
@@ -9225,7 +9226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// directly setting profile-owner or device-owner.
if (getUserProvisioningState(userId)
!= DevicePolicyManager.STATE_USER_UNMANAGED
- || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+ || newState != STATE_USER_SETUP_FINALIZED) {
throw new IllegalStateException("Not allowed to change provisioning state "
+ "unless current provisioning state is unmanaged, and new state"
+ "is finalized.");
@@ -9259,7 +9260,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE:
case DevicePolicyManager.STATE_USER_SETUP_COMPLETE:
// Can only move to finalized from these states.
- if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+ if (newState == STATE_USER_SETUP_FINALIZED) {
return;
}
break;
@@ -9271,7 +9272,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
break;
- case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
+ case STATE_USER_SETUP_FINALIZED:
// Cannot transition out of finalized.
break;
case DevicePolicyManager.STATE_USER_PROFILE_FINALIZED:
@@ -12257,87 +12258,50 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public void setPreferentialNetworkServiceEnabled(boolean enabled) {
+ public void setPreferentialNetworkServiceConfigs(
+ List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
if (!mHasFeature) {
return;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller),
- "Caller is not profile owner;"
- + " only profile owner may control the preferential network service");
- synchronized (getLockObject()) {
- final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
- caller.getUserId());
- if (requiredAdmin != null
- && requiredAdmin.mPreferentialNetworkServiceEnabled != enabled) {
- requiredAdmin.mPreferentialNetworkServiceEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
- }
- }
- updateNetworkPreferenceForUser(caller.getUserId(), enabled);
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED)
- .setBoolean(enabled)
- .write();
- }
-
- @Override
- public boolean isPreferentialNetworkServiceEnabled(int userHandle) {
- if (!mHasFeature) {
- return false;
- }
-
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller),
- "Caller is not profile owner");
- synchronized (getLockObject()) {
- final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(userHandle);
- if (requiredAdmin != null) {
- return requiredAdmin.mPreferentialNetworkServiceEnabled;
- } else {
- return false;
- }
- }
- }
-
- @Override
- public void setPreferentialNetworkServiceConfig(
- PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
- if (!mHasFeature) {
- return;
- }
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller),
- "Caller is not profile owner;"
- + " only profile owner may control the preferential network service");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller),
+ "Caller is not profile owner or device owner;"
+ + " only profile owner or device owner may control the preferential"
+ + " network service");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
caller.getUserId());
- if (!requiredAdmin.mPreferentialNetworkServiceConfig.equals(
- preferentialNetworkServiceConfig)) {
- requiredAdmin.mPreferentialNetworkServiceConfig = preferentialNetworkServiceConfig;
+ if (!requiredAdmin.mPreferentialNetworkServiceConfigs.equals(
+ preferentialNetworkServiceConfigs)) {
+ requiredAdmin.mPreferentialNetworkServiceConfigs =
+ new ArrayList<>(preferentialNetworkServiceConfigs);
saveSettingsLocked(caller.getUserId());
}
}
- updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfig);
+ updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfigs);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED)
- .setBoolean(preferentialNetworkServiceConfig.isEnabled())
+ .setBoolean(preferentialNetworkServiceConfigs
+ .stream().anyMatch(c -> c.isEnabled()))
.write();
}
@Override
- public PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() {
+ public List<PreferentialNetworkServiceConfig> getPreferentialNetworkServiceConfigs() {
if (!mHasFeature) {
- return PreferentialNetworkServiceConfig.DEFAULT;
+ return List.of(PreferentialNetworkServiceConfig.DEFAULT);
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller),
- "Caller is not profile owner");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller),
+ "Caller is not profile owner or device owner;"
+ + " only profile owner or device owner may retrieve the preferential"
+ + " network service configurations");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(caller.getUserId());
- return requiredAdmin.mPreferentialNetworkServiceConfig;
+ return requiredAdmin.mPreferentialNetworkServiceConfigs;
}
}
@@ -16458,7 +16422,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ if (apnSetting.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller));
+ } else {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ }
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm != null) {
@@ -16466,7 +16435,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
() -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
} else {
Slogf.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
- return Telephony.Carriers.INVALID_APN_ID;
+ return INVALID_APN_ID;
}
}
@@ -16479,7 +16448,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ ApnSetting apn = getApnSetting(apnId);
+ if (apn != null && apn.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
+ && apnSetting.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller));
+ } else {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ }
if (apnId < 0) {
return false;
@@ -16501,7 +16477,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ ApnSetting apn = getApnSetting(apnId);
+ if (apn != null && apn.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwner(caller));
+ } else {
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
+ }
return removeOverrideApnUnchecked(apnId);
}
@@ -16515,6 +16497,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return numDeleted > 0;
}
+ private ApnSetting getApnSetting(int apnId) {
+ if (apnId < 0) {
+ return null;
+ }
+ ApnSetting apnSetting = null;
+ Cursor cursor = mInjector.binderWithCleanCallingIdentity(
+ () -> mContext.getContentResolver().query(
+ Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)), null, null, null,
+ Telephony.Carriers.DEFAULT_SORT_ORDER));
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ apnSetting = ApnSetting.makeApnSetting(cursor);
+ if (apnSetting != null) {
+ break;
+ }
+ }
+ cursor.close();
+ }
+ return apnSetting;
+ }
+
@Override
public List<ApnSetting> getOverrideApns(@NonNull ComponentName who) {
if (!mHasFeature || !mHasTelephonyFeature) {
@@ -18092,7 +18095,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ || (hasCallingOrSelfPermission(permission.PROVISION_DEMO_DEVICE)
+ && provisioningParams.isDemoDevice()));
provisioningParams.logParams(callerPackage);
@@ -18129,8 +18134,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
disallowAddUser();
- setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
- provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ setAdminCanGrantSensorsPermissionForUserUnchecked(
+ deviceOwnerUserId, provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ setDemoDeviceStateUnchecked(deviceOwnerUserId, provisioningParams.isDemoDevice());
onProvisionFullyManagedDeviceCompleted(provisioningParams);
sendProvisioningCompletedBroadcast(
deviceOwnerUserId,
@@ -18329,6 +18335,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void setDemoDeviceStateUnchecked(@UserIdInt int userId, boolean isDemoDevice) {
+ Slogf.d(LOG_TAG, "setDemoDeviceStateUnchecked(%d, %b)",
+ userId, isDemoDevice);
+ if (!isDemoDevice) {
+ return;
+ }
+ synchronized (getLockObject()) {
+ mInjector.settingsGlobalPutStringForUser(
+ Settings.Global.DEVICE_DEMO_MODE, Integer.toString(/* value= */ 1), userId);
+ }
+ setUserProvisioningState(STATE_USER_SETUP_FINALIZED, userId);
+ }
+
private void updateAdminCanGrantSensorsPermissionCache(@UserIdInt int userId) {
synchronized (getLockObject()) {
@@ -18346,54 +18365,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void updateNetworkPreferenceForUser(int userId,
- boolean preferentialNetworkServiceEnabled) {
+ List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
if (!isManagedProfile(userId)) {
return;
}
- ProfileNetworkPreference.Builder preferenceBuilder =
- new ProfileNetworkPreference.Builder();
- if (preferentialNetworkServiceEnabled) {
- preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
- preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
- } else {
- preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
- }
List<ProfileNetworkPreference> preferences = new ArrayList<>();
- preferences.add(preferenceBuilder.build());
- mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getConnectivityManager().setProfileNetworkPreferences(
- UserHandle.of(userId), preferences,
- null /* executor */, null /* listener */));
- }
-
- private void updateNetworkPreferenceForUser(int userId,
- PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
- if (!isManagedProfile(userId)) {
- return;
- }
- ProfileNetworkPreference.Builder preferenceBuilder =
- new ProfileNetworkPreference.Builder();
- if (preferentialNetworkServiceConfig.isEnabled()) {
- if (preferentialNetworkServiceConfig.isFallbackToDefaultConnectionAllowed()) {
- preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ for (PreferentialNetworkServiceConfig preferentialNetworkServiceConfig :
+ preferentialNetworkServiceConfigs) {
+ ProfileNetworkPreference.Builder preferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ if (preferentialNetworkServiceConfig.isEnabled()) {
+ if (preferentialNetworkServiceConfig.isFallbackToDefaultConnectionAllowed()) {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ } else {
+ preferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ }
} else {
- preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
}
- } else {
- preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
- }
- List<Integer> allowedUids = Arrays.stream(
- preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect(
- Collectors.toList());
- List<Integer> excludedUids = Arrays.stream(
- preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect(
- Collectors.toList());
- preferenceBuilder.setIncludedUids(allowedUids);
- preferenceBuilder.setExcludedUids(excludedUids);
- preferenceBuilder.setPreferenceEnterpriseId(
- preferentialNetworkServiceConfig.getNetworkId());
- List<ProfileNetworkPreference> preferences = new ArrayList<>();
- preferences.add(preferenceBuilder.build());
+ List<Integer> allowedUids = Arrays.stream(
+ preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect(
+ Collectors.toList());
+ List<Integer> excludedUids = Arrays.stream(
+ preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect(
+ Collectors.toList());
+ preferenceBuilder.setIncludedUids(allowedUids);
+ preferenceBuilder.setExcludedUids(excludedUids);
+ preferenceBuilder.setPreferenceEnterpriseId(
+ preferentialNetworkServiceConfig.getNetworkId());
+
+ preferences.add(preferenceBuilder.build());
+ }
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.getConnectivityManager().setProfileNetworkPreferences(
UserHandle.of(userId), preferences,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 294dc8941983..fa2850a5157d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -41,6 +41,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -1225,12 +1226,15 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(domainVerificationService);
t.traceEnd();
+ IPackageManager iPackageManager;
t.traceBegin("StartPackageManagerService");
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
- mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
- domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
- mOnlyCore);
+ Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
+ mSystemContext, installer, domainVerificationService,
+ mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+ mPackageManagerService = pmsPair.first;
+ iPackageManager = pmsPair.second;
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}
@@ -1238,7 +1242,7 @@ public final class SystemServer implements Dumpable {
// Now that the package manager has started, register the dex load reporter to capture any
// dex files loaded by system server.
// These dex files will be optimized by the BackgroundDexOptService.
- SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);
+ SystemServerDexLoadReporter.configureSystemServerDexReporter(iPackageManager);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index b7f8c00896d4..8a9845b1c2d2 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -16,6 +16,10 @@
package com.android.server.backup;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
import static com.android.server.backup.testing.TransportData.genericTransport;
import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
@@ -312,6 +316,86 @@ public class TransportManagerTest {
}
@Test
+ public void testOnPackageChanged_whenPackageChanged_packageDisabledUnregistersTransport()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_packageEnabledRegistersTransport()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_ENABLED),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_unknownComponentStateIsIgnored()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DEFAULT),
+ 0 /*flags*/);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_unknownPackageExceptionIsIgnored()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ // empty packageName triggers Robolectric ApplicationPackageManager to throw
+ // exception as if package does not exist.
+ transportManager.onPackageChanged("", "");
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
public void testRegisterAndSelectTransport_whenTransportRegistered() throws Exception {
TransportManager transportManager =
createTransportManagerWithRegisteredTransports(null, mTransportA1);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
index aea36e555ad7..4a9948668bc4 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -16,6 +16,7 @@
package com.android.server.testing.shadows;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.NameNotFoundException;
import android.app.ApplicationPackageManager;
@@ -44,6 +45,7 @@ public class ShadowApplicationPackageManager
private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
private static final Map<String, Integer> sPackageUids = new ArrayMap<>();
private static final Map<Integer, Map<String, Integer>> sUserPackageUids = new ArrayMap<>();
+ private static final Map<String, Integer> sPackageAppEnabledStates = new ArrayMap<>();
/**
* Registers the package {@code packageName} to be returned when invoking {@link
@@ -53,6 +55,7 @@ public class ShadowApplicationPackageManager
public static void addInstalledPackage(String packageName, PackageInfo packageInfo) {
sPackageInfos.put(packageName, packageInfo);
sInstalledPackages.add(packageInfo);
+ sPackageAppEnabledStates.put(packageName, Integer.valueOf(COMPONENT_ENABLED_STATE_DEFAULT));
}
/**
@@ -77,6 +80,22 @@ public class ShadowApplicationPackageManager
}
@Override
+ protected int getApplicationEnabledSetting(String packageName) {
+ if (packageName.isEmpty()) {
+ throw new IllegalArgumentException("Robo: Package '' does not exist.");
+ }
+ if (!sPackageAppEnabledStates.containsKey(packageName)) {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ return sPackageAppEnabledStates.get(packageName);
+ }
+
+ @Override
+ protected void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+ sPackageAppEnabledStates.put(packageName, Integer.valueOf(newState)); // flags unused here.
+ }
+
+ @Override
protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
if (!sPackageInfos.containsKey(packageName)) {
@@ -115,6 +134,7 @@ public class ShadowApplicationPackageManager
public static void reset() {
sPackageInfos.clear();
sInstalledPackages.clear();
+ sPackageAppEnabledStates.clear();
org.robolectric.shadows.ShadowApplicationPackageManager.reset();
}
}
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index dcc461be0015..7017440a86bb 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -221,11 +221,14 @@ class PackageManagerComponentLabelIconOverrideTest {
@After
fun verifyExpectedResult() {
assertServiceInitialized() ?: return
- if (params.componentName != null) {
- val activityInfo = service.getActivityInfo(params.componentName, 0, userId)
- if (activityInfo != null) {
- assertThat(activityInfo.nonLocalizedLabel).isEqualTo(params.expectedLabel)
- assertThat(activityInfo.icon).isEqualTo(params.expectedIcon)
+ if (params.componentName != null && params.result !is Result.Exception) {
+ // Suppress so that failures in @After don't override the actual test failure
+ @Suppress("UNNECESSARY_SAFE_CALL")
+ service?.let {
+ val activityInfo = it.snapshotComputer()
+ .getActivityInfo(params.componentName, 0, userId)
+ assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(params.expectedLabel)
+ assertThat(activityInfo?.icon).isEqualTo(params.expectedIcon)
}
}
}
@@ -237,9 +240,12 @@ class PackageManagerComponentLabelIconOverrideTest {
Result.Changed, Result.ChangedWithoutNotify -> {
// Suppress so that failures in @After don't override the actual test failure
@Suppress("UNNECESSARY_SAFE_CALL")
- val activityInfo = service?.getActivityInfo(params.componentName, 0, userIdDifferent)
- assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
- assertThat(activityInfo?.icon).isEqualTo(DEFAULT_ICON)
+ service?.let {
+ val activityInfo = it.snapshotComputer()
+ ?.getActivityInfo(params.componentName, 0, userIdDifferent)
+ assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
+ assertThat(activityInfo?.icon).isEqualTo(DEFAULT_ICON)
+ }
}
Result.NotChanged, is Result.Exception -> {}
}.run { /*exhaust*/ }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 83ccabf03935..8f81e930d0cd 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -505,11 +505,6 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
)
}
),
- getSetByValue(
- AndroidPackage::shouldInheritKeyStoreKeys,
- ParsingPackage::setInheritKeyStoreKeys,
- true
- ),
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
getSetByValue(
AndroidPackage::isOnBackInvokedCallbackEnabled,
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
index e89c812ba1fb..4fe9cd30e4ff 100644
--- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -28,7 +28,6 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import android.graphics.Bitmap;
import android.platform.test.annotations.Presubmit;
import android.service.games.GameSession.ScreenshotCallback;
import android.testing.AndroidTestingRunner;
@@ -61,7 +60,6 @@ import java.util.concurrent.TimeUnit;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public final class GameSessionTest {
private static final long WAIT_FOR_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
- private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
@Mock
private IGameSessionController mMockGameSessionController;
@@ -101,7 +99,7 @@ public final class GameSessionTest {
}
@Override
- public void onSuccess(Bitmap bitmap) {
+ public void onSuccess() {
fail();
}
});
@@ -131,7 +129,7 @@ public final class GameSessionTest {
}
@Override
- public void onSuccess(Bitmap bitmap) {
+ public void onSuccess() {
fail();
}
});
@@ -160,7 +158,7 @@ public final class GameSessionTest {
}
@Override
- public void onSuccess(Bitmap bitmap) {
+ public void onSuccess() {
fail();
}
});
@@ -170,10 +168,10 @@ public final class GameSessionTest {
}
@Test
- public void takeScreenshot_gameManagerSuccess_returnsBitmap() throws Exception {
+ public void takeScreenshot_gameManagerSuccess() throws Exception {
doAnswer(invocation -> {
AndroidFuture result = invocation.getArgument(1);
- result.complete(GameScreenshotResult.createSuccessResult(TEST_BITMAP));
+ result.complete(GameScreenshotResult.createSuccessResult());
return null;
}).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
@@ -187,8 +185,7 @@ public final class GameSessionTest {
}
@Override
- public void onSuccess(Bitmap bitmap) {
- assertEquals(TEST_BITMAP, bitmap);
+ public void onSuccess() {
countDownLatch.countDown();
}
});
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index 32a31d0e57f7..575e3513eae4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -46,7 +46,12 @@ import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Picture;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -71,6 +76,7 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.ScreenshotHelper;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService;
@@ -87,6 +93,7 @@ import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
+import java.util.function.Consumer;
/**
@@ -114,7 +121,16 @@ public final class GameServiceProviderInstanceImplTest {
new ComponentName(GAME_B_PACKAGE, "com.package.game.b.MainActivity");
- private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
+ private static final Bitmap TEST_BITMAP;
+ static {
+ Picture picture = new Picture();
+ Canvas canvas = picture.beginRecording(200, 100);
+ Paint p = new Paint();
+ p.setColor(Color.BLACK);
+ canvas.drawCircle(10, 10, 10, p);
+ picture.endRecording();
+ TEST_BITMAP = Bitmap.createBitmap(picture);
+ }
private MockitoSession mMockingSession;
private GameServiceProviderInstance mGameServiceProviderInstance;
@@ -126,6 +142,8 @@ public final class GameServiceProviderInstanceImplTest {
private WindowManagerInternal mMockWindowManagerInternal;
@Mock
private IActivityManager mMockActivityManager;
+ @Mock
+ private ScreenshotHelper mMockScreenshotHelper;
private MockContext mMockContext;
private FakeGameClassifier mFakeGameClassifier;
private FakeGameService mFakeGameService;
@@ -192,7 +210,8 @@ public final class GameServiceProviderInstanceImplTest {
mMockWindowManagerService,
mMockWindowManagerInternal,
mFakeGameServiceConnector,
- mFakeGameSessionServiceConnector);
+ mFakeGameSessionServiceConnector,
+ mMockScreenshotHelper);
}
@After
@@ -425,6 +444,7 @@ public final class GameServiceProviderInstanceImplTest {
public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -446,6 +466,7 @@ public final class GameServiceProviderInstanceImplTest {
public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -799,27 +820,32 @@ public final class GameServiceProviderInstanceImplTest {
SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class);
SurfaceControl[] excludeLayers = new SurfaceControl[1];
excludeLayers[0] = mockOverlaySurfaceControl;
+ int taskId = 10;
when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP);
-
+ doAnswer(invocation -> {
+ Consumer<Uri> consumer = invocation.getArgument(invocation.getArguments().length - 1);
+ consumer.accept(Uri.parse("a/b.png"));
+ return null;
+ }).when(mMockScreenshotHelper).provideScreenshot(
+ any(), any(), any(), anyInt(), anyInt(), any(), anyInt(), any(), any());
mGameServiceProviderInstance.start();
- startTask(10, GAME_A_MAIN_ACTIVITY);
+ startTask(taskId, GAME_A_MAIN_ACTIVITY);
mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
- mFakeGameService.requestCreateGameSession(10);
+ mFakeGameService.requestCreateGameSession(taskId);
FakeGameSession gameSession10 = new FakeGameSession();
SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class);
when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl);
- mFakeGameSessionService.removePendingFutureForTaskId(10)
+ mFakeGameSessionService.removePendingFutureForTaskId(taskId)
.complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage));
IGameSessionController gameSessionController = getOnlyElement(
mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
- gameSessionController.takeScreenshot(10, resultFuture);
+ gameSessionController.takeScreenshot(taskId, resultFuture);
GameScreenshotResult result = resultFuture.get();
assertEquals(GameScreenshotResult.GAME_SCREENSHOT_SUCCESS, result.getStatus());
- assertEquals(TEST_BITMAP, result.getBitmap());
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index b5ad459d29f7..784f732ba3b1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -754,6 +754,57 @@ public class LocalDisplayAdapterTest {
verify(mMockedBacklight, never()).setBrightness(anyFloat());
}
+ @Test
+ public void testGetSystemPreferredDisplayMode() throws Exception {
+ SurfaceControl.DisplayMode displayMode1 = createFakeDisplayMode(0, 1920, 1080, 60f);
+ // preferred mode
+ SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 3840, 2160, 60f);
+
+ SurfaceControl.DisplayMode[] modes =
+ new SurfaceControl.DisplayMode[]{displayMode1, displayMode2};
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, 1);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+
+ Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode2)).isTrue();
+
+ // Change the display and add new preferred mode
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(2, 2340, 1080, 60f);
+ modes = new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, addedDisplayInfo};
+ display.dynamicInfo.supportedDisplayModes = modes;
+ display.dynamicInfo.preferredBootDisplayMode = 2;
+ setUpDisplay(display);
+ mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertTrue(mListener.traversalRequested);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode1);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode2);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
+
+ assertThat(matches(displayDevice.getSystemPreferredDisplayModeLocked(), addedDisplayInfo))
+ .isTrue();
+ }
+
private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
float expectedXdpi,
float expectedYDpi,
@@ -831,6 +882,16 @@ public class LocalDisplayAdapterTest {
dynamicInfo.supportedDisplayModes = modes;
dynamicInfo.activeDisplayModeId = activeMode;
}
+
+ private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode,
+ int preferredMode) {
+ address = createDisplayAddress(port);
+ info = createFakeDisplayInfo();
+ dynamicInfo.supportedDisplayModes = modes;
+ dynamicInfo.activeDisplayModeId = activeMode;
+ dynamicInfo.preferredBootDisplayMode = preferredMode;
+ }
+
}
private void setUpDisplay(FakeDisplay display) {
@@ -843,6 +904,7 @@ public class LocalDisplayAdapterTest {
.thenReturn(display.dynamicInfo);
when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token))
.thenReturn(display.desiredDisplayModeSpecs);
+ when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
}
private void updateAvailableDisplays() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
index ccfeb4c9df51..fbbb814388f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
@@ -74,6 +74,10 @@ class PackageFreezerTest {
assertThat(assertFailsWith(exceptionClass, block).message).contains(message)
}
+ private fun checkPackageStartable() {
+ pms.checkPackageStartable(pms.snapshotComputer(), TEST_PACKAGE, TEST_USER_ID)
+ }
+
@Before
@Throws(Exception::class)
fun setup() {
@@ -89,11 +93,11 @@ class PackageFreezerTest {
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
freezer.close()
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
@Test
@@ -104,16 +108,16 @@ class PackageFreezerTest {
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
freezer1.close()
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
freezer2.close()
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
@Test
@@ -123,13 +127,13 @@ class PackageFreezerTest {
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
freezer = null
System.gc()
System.runFinalization()
- pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
+ checkPackageStartable()
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index a6c7bfb456be..13199032a223 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -107,7 +107,7 @@ class PackageManagerServiceHibernationTests {
whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(true)
- pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+ pm.setPackageStoppedState(pm.snapshotComputer(), TEST_PACKAGE_NAME, false, TEST_USER_ID)
TestableLooper.get(this).processAllMessages()
@@ -133,7 +133,8 @@ class PackageManagerServiceHibernationTests {
.thenReturn(true)
try {
- pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+ pm.setPackageStoppedState(pm.snapshotComputer(), TEST_PACKAGE_NAME, false,
+ TEST_USER_ID)
TestableLooper.get(this).processAllMessages()
} catch (e: Exception) {
Assert.fail("Method throws exception when AppHibernationManager is not ready.\n$e")
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index f7b1dd5219d6..1464405cca08 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -39,8 +39,7 @@ import android.content.Context;
import android.content.pm.ApexStagedEvent;
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
+import android.content.pm.PackageManager;
import android.content.pm.StagedApexInfo;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
@@ -158,10 +157,10 @@ public class StagingManagerTest {
mStagingManager.restoreSessions(Arrays.asList(session1, session2), true);
- assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ assertThat(session1.getErrorCode()).isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed");
- assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ assertThat(session2.getErrorCode()).isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed");
}
@@ -247,12 +246,12 @@ public class StagingManagerTest {
verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
assertThat(apexSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+ "staged session supposed to be activated");
assertThat(apkSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
}
@@ -303,22 +302,22 @@ public class StagingManagerTest {
verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
assertThat(apexSession1.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. "
+ "Error: Failed for test");
assertThat(apexSession2.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't "
+ "activate nor fail. Marking it as failed anyway.");
assertThat(apexSession3.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+ "staged session supposed to be activated");
assertThat(apkSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
}
@@ -351,12 +350,12 @@ public class StagingManagerTest {
verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
assertThat(apexSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't "
+ "activate nor fail. Marking it as failed anyway.");
assertThat(apkSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
}
@@ -445,11 +444,11 @@ public class StagingManagerTest {
verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
assertThat(apexSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state");
assertThat(apkSession.getErrorCode())
- .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+ .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
}
@@ -755,7 +754,7 @@ public class StagingManagerTest {
/* isReady */ false,
/* isFailed */ false,
/* isApplied */false,
- /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.SESSION_NO_ERROR,
+ /* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN,
/* stagedSessionErrorMessage */ "no error");
StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
@@ -775,7 +774,7 @@ public class StagingManagerTest {
private boolean mIsReady = false;
private boolean mIsApplied = false;
private boolean mIsFailed = false;
- private @SessionErrorCode int mErrorCode = -1;
+ private int mErrorCode = -1;
private String mErrorMessage;
private boolean mIsDestroyed = false;
private int mParentSessionId = -1;
@@ -828,7 +827,7 @@ public class StagingManagerTest {
return this;
}
- private @SessionErrorCode int getErrorCode() {
+ private int getErrorCode() {
return mErrorCode;
}
@@ -940,7 +939,7 @@ public class StagingManagerTest {
}
@Override
- public void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage) {
+ public void setSessionFailed(int errorCode, String errorMessage) {
Preconditions.checkState(!mIsApplied, "Already marked as applied");
mIsFailed = true;
mErrorCode = errorCode;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b601d14f1f58..1ebcbe10fae6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4157,8 +4157,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetPreferentialNetworkServiceConfig_noProfileOwner() throws Exception {
assertExpectException(SecurityException.class, null,
- () -> dpm.setPreferentialNetworkServiceConfig(
- PreferentialNetworkServiceConfig.DEFAULT));
+ () -> dpm.setPreferentialNetworkServiceConfigs(
+ List.of(PreferentialNetworkServiceConfig.DEFAULT)));
}
@Test
@@ -4198,10 +4198,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
addManagedProfile(admin1, managedProfileAdminUid, admin1);
mContext.binder.callingUid = managedProfileAdminUid;
- dpm.setPreferentialNetworkServiceConfig(PreferentialNetworkServiceConfig.DEFAULT);
+ dpm.setPreferentialNetworkServiceConfigs(
+ List.of(PreferentialNetworkServiceConfig.DEFAULT));
assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
- assertThat(dpm.getPreferentialNetworkServiceConfig()
- .isEnabled()).isFalse();
+ assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0).isEnabled()).isFalse();
ProfileNetworkPreference preferenceDetails =
new ProfileNetworkPreference.Builder()
@@ -4227,8 +4227,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
addManagedProfile(admin1, managedProfileAdminUid, admin1);
mContext.binder.callingUid = managedProfileAdminUid;
- dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
- assertThat(dpm.getPreferentialNetworkServiceConfig()
+ dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled));
+ assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0)
.isEnabled()).isTrue();
ProfileNetworkPreference preferenceDetails =
new ProfileNetworkPreference.Builder()
@@ -4257,8 +4257,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.setFallbackToDefaultConnectionAllowed(false)
.setIncludedUids(new int[]{1, 2})
.build();
- dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
- assertThat(dpm.getPreferentialNetworkServiceConfig()
+ dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled));
+ assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0)
.isEnabled()).isTrue();
List<Integer> includedList = new ArrayList<>();
includedList.add(1);
@@ -4292,8 +4292,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.setExcludedUids(new int[]{1, 2})
.build();
- dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
- assertThat(dpm.getPreferentialNetworkServiceConfig()
+ dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled));
+ assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0)
.isEnabled()).isTrue();
List<Integer> excludedUids = new ArrayList<>();
excludedUids.add(1);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a9812ab9bb03..d104871f488a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -89,6 +89,7 @@ public class HdmiCecLocalDeviceTvTest {
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mTvPhysicalAddress;
private int mTvLogicalAddress;
+ private boolean mWokenUp;
@Mock
private AudioManager mAudioManager;
@@ -104,6 +105,11 @@ public class HdmiCecLocalDeviceTvTest {
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.emptyList()) {
@Override
+ void wakeUp() {
+ mWokenUp = true;
+ super.wakeUp();
+ }
+ @Override
boolean isControlEnabled() {
return true;
}
@@ -308,6 +314,22 @@ public class HdmiCecLocalDeviceTvTest {
}
@Test
+ public void handleTextViewOn_Dreaming() {
+ mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
+ mTestLooper.dispatchAll();
+ mPowerManager.setInteractive(true);
+ mWokenUp = false;
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
+ mTvLogicalAddress);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ assertThat(mWokenUp).isTrue();
+ }
+
+ @Test
public void tvSendStandbyOnSleep_Enabled() {
mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index 5185e15a8557..db602ca83f30 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -98,11 +98,8 @@ public class SystemAppUpdateTrackerTest {
private ActivityTaskManagerInternal mMockActivityTaskManager;
@Mock
private ActivityManagerInternal mMockActivityManager;
- @Mock
- private LocaleManagerBackupHelper mMockLocaleManagerBackupHelper;
- @Mock
- PackageMonitor mMockPackageMonitor;
+ PackageMonitor mPackageMonitor;
private LocaleManagerService mLocaleManagerService;
// Object under test.
@@ -114,11 +111,14 @@ public class SystemAppUpdateTrackerTest {
mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
mMockActivityManager = mock(ActivityManagerInternal.class);
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
- mMockPackageMonitor = mock(PackageMonitor.class);
- mMockLocaleManagerBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
+ LocaleManagerBackupHelper mockLocaleManagerBackupHelper =
+ mock(ShadowLocaleManagerBackupHelper.class);
+ // PackageMonitor is not needed in LocaleManagerService for these tests hence it is
+ // passed as null.
mLocaleManagerService = new LocaleManagerService(mMockContext,
mMockActivityTaskManager, mMockActivityManager,
- mMockPackageManagerInternal, mMockLocaleManagerBackupHelper, mMockPackageMonitor);
+ mMockPackageManagerInternal, mockLocaleManagerBackupHelper,
+ /* mPackageMonitor= */ null);
doReturn(DEFAULT_USER_ID).when(mMockActivityManager)
.handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
@@ -134,6 +134,9 @@ public class SystemAppUpdateTrackerTest {
mSystemAppUpdateTracker = new SystemAppUpdateTracker(mMockContext,
mLocaleManagerService, mStoragefile);
+
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(
+ mockLocaleManagerBackupHelper, mSystemAppUpdateTracker);
}
@After
@@ -148,7 +151,7 @@ public class SystemAppUpdateTrackerTest {
.when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
// Updates the app once so that it writes to the file.
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
Binder.getCallingUid());
// Clear the in-memory data of updated apps
mSystemAppUpdateTracker.getUpdatedApps().clear();
@@ -167,7 +170,7 @@ public class SystemAppUpdateTrackerTest {
DEFAULT_LOCALES)).when(mMockActivityTaskManager)
.getApplicationConfig(anyString(), anyInt());
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
Binder.getCallingUid());
assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
@@ -186,7 +189,7 @@ public class SystemAppUpdateTrackerTest {
.getApplicationConfig(anyString(), anyInt());
// first update
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
Binder.getCallingUid());
assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
@@ -195,7 +198,7 @@ public class SystemAppUpdateTrackerTest {
verifyStorageFileContents(expectedAppList);
// second update
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
Binder.getCallingUid());
// getApplicationLocales should be invoked only once on the first update.
verify(mMockActivityTaskManager, times(1))
@@ -212,7 +215,7 @@ public class SystemAppUpdateTrackerTest {
/* isUpdatedSystemApp = */false))
.when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_2), any());
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_2,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_2,
Binder.getCallingUid());
assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
@@ -231,7 +234,7 @@ public class SystemAppUpdateTrackerTest {
.when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
doReturn(null).when(mMockPackageManager).getInstallSourceInfo(anyString());
- mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
Binder.getCallingUid());
// getApplicationLocales should be never be invoked if not installer is not present.
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 3d21b74825f0..27c3ca46cb20 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -20,10 +20,15 @@ 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 static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import android.util.AtomicFile;
import android.util.Slog;
@@ -68,12 +73,17 @@ public class PackageInstallerSessionTest {
@Mock
PackageManagerService mMockPackageManagerInternal;
+ @Mock
+ Computer mSnapshot;
+
@Before
public void setUp() throws Exception {
mTmpDir = mTemporaryFolder.newFolder("PackageInstallerSessionTest");
mSessionsFile = new AtomicFile(
new File(mTmpDir.getAbsolutePath() + "/sessions.xml"), "package-session");
MockitoAnnotations.initMocks(this);
+ when(mSnapshot.getPackageUid(anyString(), anyLong(), anyInt())).thenReturn(0);
+ when(mMockPackageManagerInternal.snapshotComputer()).thenReturn(mSnapshot);
}
@Test
@@ -188,7 +198,7 @@ public class PackageInstallerSessionTest {
/* isFailed */ false,
/* isApplied */false,
/* stagedSessionErrorCode */
- PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
/* stagedSessionErrorMessage */ "some error");
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 3cda2a554e48..ec16188bfc1d 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -288,7 +288,7 @@ public class VibrationSettingsTest {
}
@Test
- public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneOnly() {
+ public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndNotification() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
@@ -296,7 +296,7 @@ public class VibrationSettingsTest {
setRingerMode(AudioManager.RINGER_MODE_SILENT);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_RINGTONE) {
+ if (usage == USAGE_RINGTONE || usage == USAGE_NOTIFICATION) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
} else {
assertVibrationNotIgnoredForUsage(usage);
@@ -305,6 +305,16 @@ public class VibrationSettingsTest {
}
@Test
+ public void shouldIgnoreVibration_withRingerModeSilentAndBypassFlag_allowsAllVibrations() {
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
+
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
+ }
+ }
+
+ @Test
public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index e46e28199d5a..92736c517782 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1136,19 +1136,23 @@ public class VibratorManagerServiceTest {
}
@Test
- public void onExternalVibration_setsExternalControl() {
+ public void onExternalVibration_setsExternalControl() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
createSystemReadyService();
+ IBinder binderToken = mock(IBinder.class);
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
- mock(IExternalVibrationController.class));
+ mock(IExternalVibrationController.class), binderToken);
int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
mExternalVibratorService.onExternalVibrationStop(externalVibration);
assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
assertEquals(Arrays.asList(false, true, false),
mVibratorProviders.get(1).getExternalControlStates());
+
+ verify(binderToken).linkToDeath(any(), eq(0));
+ verify(binderToken).unlinkToDeath(any(), eq(0));
}
@Test
@@ -1160,17 +1164,19 @@ public class VibratorManagerServiceTest {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
createSystemReadyService();
+ IBinder firstToken = mock(IBinder.class);
+ IBinder secondToken = mock(IBinder.class);
IExternalVibrationController firstController = mock(IExternalVibrationController.class);
IExternalVibrationController secondController = mock(IExternalVibrationController.class);
ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
- firstController);
+ firstController, firstToken);
int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);
AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build();
ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME,
- ringtoneAudioAttrs, secondController);
+ ringtoneAudioAttrs, secondController, secondToken);
int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
@@ -1180,6 +1186,17 @@ public class VibratorManagerServiceTest {
// Set external control called only once.
assertEquals(Arrays.asList(false, true),
mVibratorProviders.get(1).getExternalControlStates());
+
+ mExternalVibratorService.onExternalVibrationStop(secondVibration);
+ mExternalVibratorService.onExternalVibrationStop(firstVibration);
+ assertEquals(Arrays.asList(false, true, false),
+ mVibratorProviders.get(1).getExternalControlStates());
+
+ verify(firstToken).linkToDeath(any(), eq(0));
+ verify(firstToken).unlinkToDeath(any(), eq(0));
+
+ verify(secondToken).linkToDeath(any(), eq(0));
+ verify(secondToken).unlinkToDeath(any(), eq(0));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 71f8b8de032b..67382bfacab4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -432,8 +433,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(
- any(ActivityManager.RecentTaskInfo.class))).thenReturn(false);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt()))
+ .thenReturn(INVALID_TASK_ID);
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
@@ -971,8 +972,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_FirstChannelWithFgndTaskStartsPermDialog()
throws Exception {
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG_NO_CHANNELS,
@@ -985,8 +985,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_SecondChannelWithFgndTaskDoesntStartPermDialog()
throws Exception {
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(true);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
assertTrue(mBinderService.getNumNotificationChannelsForPackage(PKG, mUid, true) > 0);
final NotificationChannel channel =
@@ -1001,8 +1000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_FirstChannelWithBgndTaskDoesntStartPermDialog()
throws Exception {
reset(mPermissionPolicyInternal);
- when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
- ActivityManager.RecentTaskInfo.class))).thenReturn(false);
+ when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
@@ -2707,13 +2705,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testUpdateAppNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
-
- // should not trigger a broadcast
- when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
- mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+ when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_DEFAULT);
// should trigger a broadcast
- mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
Thread.sleep(500);
waitForIdle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -2722,7 +2717,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
- assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
}
@Test
@@ -2739,7 +2734,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// should not trigger a broadcast
when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
- mService.mAppOpsCallback.opChanged(0, mUid, PKG);
// should trigger a broadcast
mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index a83887202a7d..2ba587d21163 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -606,9 +606,9 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
@Test
public void testUpdateAppNotifyCreatorBlock() throws Exception {
- when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
- mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
Thread.sleep(500);
waitForIdle();
@@ -618,14 +618,14 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
- assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
}
@Test
public void testUpdateAppNotifyCreatorUnblock() throws Exception {
- when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
- mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
Thread.sleep(500);
waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index a24ba0d1e1c2..50151bfb7191 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -89,6 +89,10 @@ public class PermissionHelperTest extends UiServiceTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true);
+ PackageInfo testPkgInfo = new PackageInfo();
+ testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS };
+ when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
+ .thenReturn(testPkgInfo);
}
// TODO (b/194833441): Remove when the migration is enabled
@@ -384,6 +388,22 @@ public class PermissionHelperTest extends UiServiceTestCase {
}
@Test
+ public void testSetNotificationPermission_doesntRequestNotChanged() throws Exception {
+ when(mPmi.checkPermission(anyString(), anyString(), anyInt()))
+ .thenReturn(PERMISSION_GRANTED);
+ PackageInfo testPkgInfo = new PackageInfo();
+ testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.RECORD_AUDIO };
+ when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
+ .thenReturn(testPkgInfo);
+ mPermissionHelper.setNotificationPermission("pkg", 10, false, false);
+
+ verify(mPmi, never()).checkPermission(
+ eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10));
+ verify(mPermManager, never()).revokeRuntimePermission(
+ eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
+ }
+
+ @Test
public void testIsPermissionFixed() throws Exception {
when(mPermManager.getPermissionFlags(anyString(),
eq(Manifest.permission.POST_NOTIFICATIONS),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8f1eed89647c..7d5a0d0bf84d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1870,46 +1870,46 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception {
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- USER.getIdentifier()).getList().size());
+ UID_N_MR1).getList().size());
}
@Test
- public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing()
+ public void testGetChannelsBypassingDnd_noChannelsForUidBypassing()
throws Exception {
- int user = 9;
+ int uid = 222;
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_MAX);
channel.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() {
- int user = USER.getIdentifier();
+ int uid = UID_N_MR1;
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
NotificationChannel channel1 = new NotificationChannel("id1", "name1",
NotificationManager.IMPORTANCE_MAX);
channel1.setBypassDnd(true);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true);
assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// disable group
ncg.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ false);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() {
- int user = USER.getIdentifier();
+ int uid = UID_N_MR1;
NotificationChannel channel1 = new NotificationChannel("id1", "name1",
NotificationManager.IMPORTANCE_MAX);
NotificationChannel channel2 = new NotificationChannel("id2", "name2",
@@ -1920,22 +1920,22 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
channel3.setBypassDnd(true);
// has DND access, so can set bypassDnd attribute
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
- mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel3, true, true);
assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// setBypassDnd false for some channels
channel1.setBypassDnd(false);
channel2.setBypassDnd(false);
assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
// setBypassDnd false for rest of the channels
channel3.setBypassDnd(false);
assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
- user).getList().size());
+ uid).getList().size());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index abcc8c1e99cb..8ac729e29424 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -121,7 +121,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
// Create a notification record with the people String array as the
// bundled extras, and the numbers ArraySet as additional phone numbers.
- private NotificationRecord getRecordWithPeopleInfo(String[] people,
+ private NotificationRecord getCallRecordWithPeopleInfo(String[] people,
ArraySet<String> numbers) {
// set up notification record
NotificationRecord r = mock(NotificationRecord.class);
@@ -131,6 +131,8 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(sbn.getNotification()).thenReturn(notification);
when(r.getSbn()).thenReturn(sbn);
when(r.getPhoneNumbers()).thenReturn(numbers);
+ when(r.getCriticality()).thenReturn(CriticalNotificationExtractor.NORMAL);
+ when(r.isCategory(CATEGORY_CALL)).thenReturn(true);
return r;
}
@@ -350,11 +352,41 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
}
@Test
+ public void testRepeatCallers_checksPhoneNumbers() {
+ // set up telephony manager behavior
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+ // first, record a phone call from a telephone number
+ String[] callNumber = new String[]{"tel:12345678910"};
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(callNumber, null));
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // make sure that a record with the phone number in extras is correctly allowed through
+ NotificationRecord r = getCallRecordWithPeopleInfo(callNumber, null);
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+
+ // make sure that a record with the phone number in the phone numbers array is also
+ // allowed through
+ NotificationRecord r2 = getCallRecordWithPeopleInfo(new String[]{"some_contact_uri"},
+ new ArraySet<>(new String[]{"12345678910"}));
+ assertFalse(mZenModeFiltering.shouldIntercept(
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r2));
+
+ // A record with the phone number in neither of the above should be intercepted
+ NotificationRecord r3 = getCallRecordWithPeopleInfo(new String[]{"tel:10987654321"},
+ new ArraySet<>(new String[]{"15555555555"}));
+ assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r3));
+ }
+
+ @Test
public void testMatchesCallFilter_repeatCallers_directMatch() {
// after calls given an email with an exact string match, make sure that
// matchesCallFilter returns the right thing
String[] mailSource = new String[]{"mailto:hello.world"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(mailSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -377,7 +409,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:+1-617-555-1212"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -421,7 +453,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:%2B16175551212"};
- mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
+ mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -468,7 +500,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
String[] contactSource = new String[]{"content://contacts/lookup/uri-here"};
ArraySet<String> contactNumbers = new ArraySet<>(
new String[]{"1-617-555-1212", "1-617-555-3434"});
- NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers);
+ NotificationRecord record = getCallRecordWithPeopleInfo(contactSource, contactNumbers);
record.mergePhoneNumbers(contactNumbers);
mZenModeFiltering.recordCall(record);
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
index 75479de26b1d..cf571815b4cb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
@@ -36,6 +36,9 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Test class for {@link KeyCombinationManager}.
*
@@ -47,16 +50,18 @@ import org.junit.Test;
public class KeyCombinationTests {
private KeyCombinationManager mKeyCombinationManager;
- private boolean mAction1Triggered = false;
- private boolean mAction2Triggered = false;
- private boolean mAction3Triggered = false;
+ private final CountDownLatch mAction1Triggered = new CountDownLatch(1);
+ private final CountDownLatch mAction2Triggered = new CountDownLatch(1);
+ private final CountDownLatch mAction3Triggered = new CountDownLatch(1);
private boolean mPreCondition = true;
private static final long SCHEDULE_TIME = 300;
+ private Handler mHandler;
@Before
public void setUp() {
- mKeyCombinationManager = new KeyCombinationManager();
+ mHandler = new Handler(Looper.getMainLooper());
+ mKeyCombinationManager = new KeyCombinationManager(mHandler);
initKeyCombinationRules();
}
@@ -67,7 +72,7 @@ public class KeyCombinationTests {
KEYCODE_POWER) {
@Override
void execute() {
- mAction1Triggered = true;
+ mAction1Triggered.countDown();
}
@Override
@@ -86,12 +91,17 @@ public class KeyCombinationTests {
@Override
void execute() {
- mAction2Triggered = true;
+ mAction2Triggered.countDown();
}
@Override
void cancel() {
}
+
+ @Override
+ long getKeyInterceptDelayMs() {
+ return 0;
+ }
});
// Rule 3 : power + volume_up schedule and trigger action after timeout.
@@ -100,10 +110,9 @@ public class KeyCombinationTests {
final Runnable mAction = new Runnable() {
@Override
public void run() {
- mAction3Triggered = true;
+ mAction3Triggered.countDown();
}
};
- final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
void execute() {
@@ -149,60 +158,74 @@ public class KeyCombinationTests {
}
@Test
- public void testTriggerRule() {
+ public void testTriggerRule() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
- assertTrue(mAction1Triggered);
+ assertTrue(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
- assertTrue(mAction2Triggered);
+ assertTrue(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP, SCHEDULE_TIME + 50);
- assertTrue(mAction3Triggered);
+ assertTrue(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
/**
* Nothing should happen if there is no definition.
*/
@Test
- public void testNotTrigger_NoRule() {
+ public void testNotTrigger_NoRule() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_BACK, eventTime, KEYCODE_VOLUME_DOWN);
- assertFalse(mAction1Triggered);
- assertFalse(mAction2Triggered);
- assertFalse(mAction3Triggered);
+ assertFalse(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
+ assertFalse(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
+ assertFalse(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
/**
* Nothing should happen if the interval of press time is too long.
*/
@Test
- public void testNotTrigger_Interval() {
+ public void testNotTrigger_Interval() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis();
final long earlyEventTime = eventTime - 200; // COMBINE_KEY_DELAY_MILLIS = 150;
pressKeys(earlyEventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
- assertFalse(mAction1Triggered);
+ assertFalse(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
/**
* Nothing should happen if the condition is false.
*/
@Test
- public void testNotTrigger_Condition() {
+ public void testNotTrigger_Condition() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis();
// we won't trigger action 2 because the condition is false.
mPreCondition = false;
pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
- assertFalse(mAction2Triggered);
+ assertFalse(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
/**
* Nothing should happen if the keys released too early.
*/
@Test
- public void testNotTrigger_EarlyRelease() {
+ public void testNotTrigger_EarlyRelease() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP);
- assertFalse(mAction3Triggered);
+ assertFalse(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * The KeyInterceptTimeout should return the max timeout value.
+ */
+ @Test
+ public void testKeyInterceptTimeout() {
+ final long eventTime = SystemClock.uptimeMillis();
+ final KeyEvent firstKeyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
+ KEYCODE_VOLUME_UP, 0 /* repeat */, 0 /* metaState */);
+ // Press KEYCODE_VOLUME_UP would activate rule2 and rule3,
+ // and rule2's intercept delay is 0.
+ mKeyCombinationManager.interceptKey(firstKeyDown, true);
+ assertTrue(mKeyCombinationManager.getKeyInterceptTimeout(KEYCODE_VOLUME_UP) > eventTime);
}
-}
+} \ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a89c5a1fbf1f..40ab8eb70c04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2217,6 +2217,19 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(activity.pictureInPictureArgs.isLaunchIntoPip());
}
+ @Test
+ public void testTransferLaunchCookieWhenFinishing() {
+ final ActivityRecord activity1 = createActivityWithTask();
+ final Binder launchCookie = new Binder();
+ activity1.mLaunchCookie = launchCookie;
+ final ActivityRecord activity2 = createActivityRecord(activity1.getTask());
+ activity1.setState(PAUSED, "test");
+ activity1.makeFinishingLocked();
+
+ assertEquals(launchCookie, activity2.mLaunchCookie);
+ assertNull(activity1.mLaunchCookie);
+ }
+
private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
boolean shouldUpdate, boolean activityChange) {
reset(activity.app);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 0c8394e75fbc..24ff3f96100a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -41,9 +41,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -58,8 +60,6 @@ import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
import org.junit.After;
import org.junit.Before;
@@ -113,7 +113,7 @@ public class ActivityStartInterceptorTest {
@Mock
private KeyguardManager mKeyguardManager;
@Mock
- private PackageManagerService mPackageManager;
+ private IPackageManager mPackageManager;
@Mock
private ActivityManagerInternal mAmInternal;
@Mock
@@ -126,7 +126,7 @@ public class ActivityStartInterceptorTest {
new SparseArray<>();
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mService.mAmInternal = mAmInternal;
mInterceptor = new ActivityStartInterceptor(
@@ -279,7 +279,7 @@ public class ActivityStartInterceptorTest {
}
@Test
- public void testHarmfulAppWarning() {
+ public void testHarmfulAppWarning() throws RemoteException {
// GIVEN the package we're about to launch has a harmful app warning set
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn("This app is bad");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 6fe2d337e4a0..f9aa4b17bc2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -308,12 +308,12 @@ public class ActivityStarterTests extends WindowTestsBase {
}
private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
- return prepareStarter(launchFlags, true /* mockGetLaunchStack */, LAUNCH_MULTIPLE);
+ return prepareStarter(launchFlags, true /* mockGetRootTask */, LAUNCH_MULTIPLE);
}
private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
- boolean mockGetLaunchStack) {
- return prepareStarter(launchFlags, mockGetLaunchStack, LAUNCH_MULTIPLE);
+ boolean mockGetRootTask) {
+ return prepareStarter(launchFlags, mockGetRootTask, LAUNCH_MULTIPLE);
}
private void setupImeWindow() {
@@ -326,20 +326,20 @@ public class ActivityStarterTests extends WindowTestsBase {
* Creates a {@link ActivityStarter} with default parameters and necessary mocks.
*
* @param launchFlags The intent flags to launch activity.
- * @param mockGetLaunchStack Whether to mock {@link RootWindowContainer#getLaunchRootTask} for
+ * @param mockGetRootTask Whether to mock {@link RootWindowContainer#getOrCreateRootTask} for
* always launching to the testing stack. Set to false when allowing
* the activity can be launched to any stack that is decided by real
* implementation.
* @return A {@link ActivityStarter} with default setup.
*/
private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
- boolean mockGetLaunchStack, int launchMode) {
+ boolean mockGetRootTask, int launchMode) {
// always allow test to start activity.
doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
- if (mockGetLaunchStack) {
+ if (mockGetRootTask) {
// Instrument the stack and task used.
final Task stack = mRootWindowContainer.getDefaultTaskDisplayArea()
.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -347,9 +347,9 @@ public class ActivityStarterTests extends WindowTestsBase {
// Direct starter to use spy stack.
doReturn(stack).when(mRootWindowContainer)
- .getLaunchRootTask(any(), any(), any(), anyBoolean());
- doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), any(),
- anyBoolean(), any(), anyInt());
+ .getOrCreateRootTask(any(), any(), any(), anyBoolean());
+ doReturn(stack).when(mRootWindowContainer).getOrCreateRootTask(any(), any(), any(),
+ any(), anyBoolean(), any(), anyInt());
}
// Set up mock package manager internal and make sure no unmocked methods are called
@@ -434,7 +434,7 @@ public class ActivityStarterTests extends WindowTestsBase {
public void testSplitScreenDeliverToTop() {
final ActivityStarter starter = prepareStarter(
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP,
- false /* mockGetLaunchStack */);
+ false /* mockGetRootTask */);
final Pair<ActivityRecord, ActivityRecord> activities = createActivitiesInSplit();
final ActivityRecord splitPrimaryFocusActivity = activities.first;
final ActivityRecord splitSecondReusableActivity = activities.second;
@@ -790,7 +790,7 @@ public class ActivityStarterTests extends WindowTestsBase {
finishingTopActivity.finishing = true;
// Launch the bottom task of the target root task.
- prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */)
+ prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */)
.setReason("testBringTaskToFrontWhenFocusedTaskIsFinishing")
.setIntent(activity.intent.addFlags(
FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
@@ -809,7 +809,7 @@ public class ActivityStarterTests extends WindowTestsBase {
@Test
public void testDeliverIntentToTopActivityOfNonTopDisplay() {
final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
- false /* mockGetLaunchStack */);
+ false /* mockGetRootTask */);
// Create a secondary display at bottom.
final TestDisplayContent secondaryDisplay =
@@ -849,7 +849,7 @@ public class ActivityStarterTests extends WindowTestsBase {
@Test
public void testBringTaskToFrontOnSecondaryDisplay() {
final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
- false /* mockGetLaunchStack */);
+ false /* mockGetRootTask */);
// Create a secondary display with an activity.
final TestDisplayContent secondaryDisplay =
@@ -943,7 +943,7 @@ public class ActivityStarterTests extends WindowTestsBase {
@Test
public void testReparentTopFocusedActivityToSecondaryDisplay() {
final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
- false /* mockGetLaunchStack */);
+ false /* mockGetRootTask */);
// Create a secondary display at bottom.
final TestDisplayContent secondaryDisplay = addNewDisplayContentAt(POSITION_BOTTOM);
@@ -1076,7 +1076,7 @@ public class ActivityStarterTests extends WindowTestsBase {
@Test
public void testTargetStackInSplitScreen() {
final ActivityStarter starter =
- prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetLaunchStack */);
+ prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityOptions options = ActivityOptions.makeBasic();
final ActivityRecord[] outActivity = new ActivityRecord[1];
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 2ea7fdaf6348..eb6395b46120 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -36,6 +36,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -422,6 +423,7 @@ public class AppTransitionTests extends WindowTestsBase {
public void testActivityRecordReparentToTaskFragment() {
final ActivityRecord activity = createActivityRecord(mDc);
final SurfaceControl activityLeash = mock(SurfaceControl.class);
+ doNothing().when(activity).setDropInputMode(anyInt());
activity.setVisibility(true);
activity.setSurfaceControl(activityLeash);
final Task task = activity.getTask();
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 12f987dfcd8d..8e990f7763c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -77,7 +77,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -108,6 +107,7 @@ import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.hardware.display.VirtualDisplay;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -1370,7 +1370,9 @@ public class DisplayContentTests extends WindowTestsBase {
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
false /* forceUpdate */));
- assertNotNull(mDisplayContent.getAsyncRotationController());
+ final AsyncRotationController asyncRotationController =
+ mDisplayContent.getAsyncRotationController();
+ assertNotNull(asyncRotationController);
assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
// Notification shade may have its own view animation in real case so do not fade out it.
@@ -1443,6 +1445,7 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(mAppWindow);
LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
app.token, app.token, mDisplayContent.mDisplayId);
+ assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken));
assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
@@ -1936,12 +1939,10 @@ public class DisplayContentTests extends WindowTestsBase {
// Preparation: Simulate snapshot IME surface.
spyOn(mWm.mTaskSnapshotController);
- doReturn(mock(SurfaceControl.ScreenshotHardwareBuffer.class)).when(
- mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any());
- final SurfaceControl imeSurface = mock(SurfaceControl.class);
- spyOn(imeSurface);
- doReturn(true).when(imeSurface).isValid();
- doReturn(imeSurface).when(mDisplayContent).createImeSurface(any(), any());
+ SurfaceControl.ScreenshotHardwareBuffer mockHwBuffer = mock(
+ SurfaceControl.ScreenshotHardwareBuffer.class);
+ doReturn(mock(HardwareBuffer.class)).when(mockHwBuffer).getHardwareBuffer();
+ doReturn(mockHwBuffer).when(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any());
// Preparation: Simulate snapshot Task.
ActivityRecord act1 = createActivityRecord(mDisplayContent);
@@ -1967,21 +1968,18 @@ public class DisplayContentTests extends WindowTestsBase {
final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2");
appWin2.setHasSurface(true);
assertTrue(appWin2.canBeImeTarget());
- doReturn(true).when(appWin1).isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ doReturn(true).when(appWin1).isClosing();
+ doReturn(true).when(appWin1).inAppOrRecentsTransition();
// Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will
- // be shown at this time.
- final Transaction t = mDisplayContent.getPendingTransaction();
- spyOn(t);
+ // be attached and shown on the display at this time.
mDisplayContent.computeImeTarget(true);
assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- verify(mDisplayContent, atLeast(1)).attachAndShowImeScreenshotOnTarget();
+ verify(mDisplayContent, atLeast(1)).showImeScreenshot();
verify(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(appWin1.getTask());
assertNotNull(mDisplayContent.mImeScreenshot);
- verify(t).show(mDisplayContent.mImeScreenshot);
}
@UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java
index 66139e6b483a..6a39d5608a19 100644
--- a/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java
+++ b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java
@@ -20,6 +20,8 @@ import static org.mockito.Mockito.mock;
import android.view.SurfaceControl;
+import org.mockito.Mockito;
+
/**
* Stubbed {@link SurfaceControl.Builder} class that returns a mocked SurfaceControl instance
* that can be used for unit testing.
@@ -32,6 +34,8 @@ class MockSurfaceControlBuilder extends SurfaceControl.Builder {
@Override
public SurfaceControl build() {
- return mock(SurfaceControl.class);
+ SurfaceControl mockSurfaceControl = mock(SurfaceControl.class);
+ Mockito.doReturn(true).when(mockSurfaceControl).isValid();
+ return mockSurfaceControl;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 19247604ad10..9d2eb26f5f21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -101,14 +101,63 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- mPolicy.addNonHighRefreshRatePackage("com.android.test");
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE, LOW_REFRESH_RATE);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- mPolicy.removeNonHighRefreshRatePackage("com.android.test");
+ mPolicy.removeRefreshRateRangeForPackage("com.android.test");
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testCameraRange() {
+ final WindowState cameraUsingWindow = createWindow("cameraUsingWindow");
+ cameraUsingWindow.mAttrs.packageName = "com.android.test";
+ parcelLayoutParams(cameraUsingWindow);
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE, MID_REFRESH_RATE);
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(MID_REFRESH_RATE,
+ mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ mPolicy.removeRefreshRateRangeForPackage("com.android.test");
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testCameraRange_OutOfRange() {
+ final WindowState cameraUsingWindow = createWindow("cameraUsingWindow");
+ cameraUsingWindow.mAttrs.packageName = "com.android.test";
+ parcelLayoutParams(cameraUsingWindow);
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE - 10, HI_REFRESH_RATE + 10);
+ assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(HI_REFRESH_RATE,
+ mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ mPolicy.removeRefreshRateRangeForPackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
@@ -162,7 +211,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
overrideWindow.mAttrs.packageName = "com.android.test";
overrideWindow.mAttrs.preferredDisplayModeId = HI_MODE_ID;
parcelLayoutParams(overrideWindow);
- mPolicy.addNonHighRefreshRatePackage("com.android.test");
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE, LOW_REFRESH_RATE);
assertEquals(HI_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
@@ -178,7 +228,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
overrideWindow.mAttrs.packageName = "com.android.test";
overrideWindow.mAttrs.preferredRefreshRate = HI_REFRESH_RATE;
parcelLayoutParams(overrideWindow);
- mPolicy.addNonHighRefreshRatePackage("com.android.test");
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE, LOW_REFRESH_RATE);
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
@@ -257,7 +308,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
cameraUsingWindow.mAttrs.packageName = "com.android.test";
parcelLayoutParams(cameraUsingWindow);
- mPolicy.addNonHighRefreshRatePackage("com.android.test");
+ mPolicy.addRefreshRateRangeForPackage("com.android.test",
+ LOW_REFRESH_RATE, LOW_REFRESH_RATE);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index acceadf8c499..99ba3b806d61 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -949,7 +949,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
LaunchParamsController.LaunchParams launchParams =
new LaunchParamsController.LaunchParams();
launchParams.mPreferredTaskDisplayArea = taskDisplayArea;
- Task root = mRootWindowContainer.getLaunchRootTask(null /* r */, null /* options */,
+ Task root = mRootWindowContainer.getOrCreateRootTask(null /* r */, null /* options */,
null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams,
0 /* launchParams */);
assertEquals(taskDisplayArea, root.getTaskDisplayArea());
@@ -957,7 +957,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
// Making sure still getting the root task from the preferred TDA when passing in a
// launching activity.
ActivityRecord r = new ActivityBuilder(mAtm).build();
- root = mRootWindowContainer.getLaunchRootTask(r, null /* options */,
+ root = mRootWindowContainer.getOrCreateRootTask(r, null /* options */,
null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams,
0 /* launchParams */);
assertEquals(taskDisplayArea, root.getTaskDisplayArea());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index cb858845e03e..33b236669ec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.wm.RunningTasks.FLAG_ALLOWED;
import static com.android.server.wm.RunningTasks.FLAG_CROSS_USERS;
import static com.android.server.wm.RunningTasks.FLAG_KEEP_INTENT_EXTRA;
@@ -81,7 +82,9 @@ public class RunningTasksTest extends WindowTestsBase {
rootTasks.add(task);
}, false /* traverseTopToBottom */);
for (int i = 0; i < numTasks; i++) {
- createTask(rootTasks.get(i % numStacks), ".Task" + i, i, activeTime++, null);
+ final Task task =
+ createTask(rootTasks.get(i % numStacks), ".Task" + i, i, activeTime++, null);
+ doReturn(false).when(task).isVisible();
}
// Ensure that the latest tasks were returned in order of decreasing last active time,
@@ -158,6 +161,36 @@ public class RunningTasksTest extends WindowTestsBase {
}
}
+ @Test
+ public void testUpdateLastActiveTimeOfVisibleTasks() {
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final int numTasks = 10;
+ final ArrayList<Task> tasks = new ArrayList<>();
+ for (int i = 0; i < numTasks; i++) {
+ final Task task = createTask(null, ".Task" + i, i, i, null);
+ doReturn(false).when(task).isVisible();
+ tasks.add(task);
+ }
+
+ final Task visibleTask = tasks.get(0);
+ doReturn(true).when(visibleTask).isVisible();
+
+ final Task focusedTask = tasks.get(1);
+ doReturn(true).when(focusedTask).isVisible();
+ doReturn(true).when(focusedTask).isFocused();
+
+ // Ensure that the last active time of visible tasks were updated while the focused one had
+ // the largest last active time.
+ final int numFetchTasks = 5;
+ final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
+ -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numFetchTasks);
+ assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
+ assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
+ }
+
/**
* Create a task with a single activity in it, with the given last active time.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index 46ef7ed092de..d27581865e26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -17,11 +17,13 @@
package com.android.server.wm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.Parcel;
import android.view.InputWindowHandle;
@@ -135,6 +137,12 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
+ @NonNull
+ public SurfaceControl.Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
return this;
}
@@ -275,6 +283,13 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
+ @NonNull
+ public SurfaceControl.Transaction setBuffer(@NonNull SurfaceControl sc,
+ @Nullable HardwareBuffer buffer) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 8474c3829827..c4547f654c33 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -32,6 +32,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.isIndependent;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -328,6 +329,7 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord act = createActivityRecord(tasks[i]);
// alternate so that the transition doesn't get promoted to the display area
act.mVisibleRequested = (i % 2) == 0; // starts invisible
+ act.visibleIgnoringKeyguard = (i % 2) == 0;
if (i == showWallpaperTask) {
doReturn(true).when(act).showWallpaper();
}
@@ -490,6 +492,86 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testOpenOpaqueTask() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task newTask = createTask(mDisplayContent);
+ doReturn(false).when(newTask).isTranslucent(any());
+ final Task oldTask = createTask(mDisplayContent);
+ doReturn(false).when(oldTask).isTranslucent(any());
+
+ final ActivityRecord closing = createActivityRecord(oldTask);
+ closing.setOccludesParent(true);
+ final ActivityRecord opening = createActivityRecord(newTask);
+ opening.setOccludesParent(false);
+ // Start states.
+ changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ fillChangeMap(changes, newTask);
+ // End states.
+ closing.mVisibleRequested = true;
+ opening.mVisibleRequested = true;
+
+ final int transit = transition.mType;
+ int flags = 0;
+
+ // Check basic both tasks participating
+ participants.add(oldTask);
+ participants.add(newTask);
+ ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ assertEquals(2, info.getChanges().size());
+ assertEquals(transit, info.getType());
+
+ assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) == 0);
+ assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
+ }
+
+ @Test
+ public void testOpenTranslucentTask() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task newTask = createTask(mDisplayContent);
+ doReturn(true).when(newTask).isTranslucent(any());
+ final Task oldTask = createTask(mDisplayContent);
+ doReturn(false).when(oldTask).isTranslucent(any());
+
+ final ActivityRecord closing = createActivityRecord(oldTask);
+ closing.setOccludesParent(true);
+ final ActivityRecord opening = createActivityRecord(newTask);
+ opening.setOccludesParent(false);
+ // Start states.
+ changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ fillChangeMap(changes, newTask);
+ // End states.
+ closing.mVisibleRequested = true;
+ opening.mVisibleRequested = true;
+
+ final int transit = transition.mType;
+ int flags = 0;
+
+ // Check basic both tasks participating
+ participants.add(oldTask);
+ participants.add(newTask);
+ ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ assertEquals(2, info.getChanges().size());
+ assertEquals(transit, info.getType());
+
+ assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) != 0);
+ assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
+ }
+
+ @Test
public void testTimeout() {
final TransitionController controller = new TransitionController(mAtm,
mock(TaskSnapshotController.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index e70dd08af8a1..e4f691b338e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -29,6 +29,8 @@ import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -81,6 +83,7 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.InputConfig;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
@@ -754,10 +757,15 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(win0.canReceiveTouchInput());
}
+ private boolean testFlag(int flags, int test) {
+ return (flags & test) == test;
+ }
+
@Test
public void testUpdateInputWindowHandle() {
final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ win.mAttrs.flags = FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH;
final InputWindowHandle handle = new InputWindowHandle(
win.mInputWindowHandle.getInputApplicationHandle(), win.getDisplayId());
final InputWindowHandleWrapper handleWrapper = new InputWindowHandleWrapper(handle);
@@ -767,13 +775,14 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win);
assertTrue(handleWrapper.isChanged());
+ assertTrue(testFlag(handle.inputConfig, InputConfig.WATCH_OUTSIDE_TOUCH));
+ assertFalse(testFlag(handle.inputConfig, InputConfig.PREVENT_SPLITTING));
+ assertTrue(testFlag(handle.inputConfig, InputConfig.DISABLE_USER_ACTIVITY));
// The window of standard resizable task should not use surface crop as touchable region.
assertFalse(handle.replaceTouchableRegionWithCrop);
assertEquals(inputChannelToken, handle.token);
assertEquals(win.mActivityRecord.getInputApplicationHandle(false /* update */),
handle.inputApplicationHandle);
- assertEquals(win.mAttrs.inputFeatures, handle.inputFeatures);
- assertEquals(win.isVisible(), handle.visible);
final SurfaceControl sc = mock(SurfaceControl.class);
final SurfaceControl.Transaction transaction = mSystemServicesTestRule.mTransaction;
@@ -799,15 +808,13 @@ public class WindowStateTests extends WindowTestsBase {
assertEquals(rotatedBounds, handle.touchableRegion.getBounds());
// Populate as an overlay to disable the input of window.
- InputMonitor.populateOverlayInputInfo(handleWrapper, false /* isVisible */);
+ InputMonitor.populateOverlayInputInfo(handleWrapper);
// The overlay attributes should be set.
assertTrue(handleWrapper.isChanged());
- assertFalse(handle.focusable);
- assertFalse(handle.visible);
+ assertFalse(handleWrapper.isFocusable());
assertNull(handle.token);
assertEquals(0L, handle.dispatchingTimeoutMillis);
- assertEquals(WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL,
- handle.inputFeatures);
+ assertTrue(testFlag(handle.inputConfig, InputConfig.NO_INPUT_CHANNEL));
}
@Test
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1999cfc706b4..b290669e103e 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -640,7 +640,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
Slog.e(TAG, "unknown state " + state);
return;
}
- removeMessages(MSG_UPDATE_STATE);
+ if (configured == 0) removeMessages(MSG_UPDATE_STATE);
if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
Message msg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1 = connected;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 84bd98347dab..a597fc69a340 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -97,6 +97,7 @@ final class HotwordDetectionConnection {
private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
+ private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
@@ -772,7 +773,8 @@ final class HotwordDetectionConnection {
ServiceConnection createLocked() {
ServiceConnection connection =
new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
- IHotwordDetectionService.Stub::asInterface, ++mRestartCount);
+ IHotwordDetectionService.Stub::asInterface,
+ mRestartCount++ % MAX_ISOLATED_PROCESS_NUMBER);
connection.connect();
updateAudioFlinger(connection, mAudioFlinger);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
new file mode 100644
index 000000000000..de0b9606045e
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * A utility class for logging hotword statistics event.
+ */
+public final class HotwordMetricsLogger {
+
+ private HotwordMetricsLogger() {
+ // Class only contains static utility functions, and should not be instantiated
+ }
+
+ /**
+ * Logs information related to create hotword detector.
+ */
+ public static void writeDetectorCreateEvent(int detectorType, boolean isCreated, int uid) {
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_CREATE_REQUESTED, detectorType,
+ isCreated, uid);
+ }
+
+ /**
+ * Logs information related to hotword detection service init result.
+ */
+ public static void writeServiceInitResultEvent(int detectorType, int result) {
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED,
+ detectorType, result);
+ }
+
+ /**
+ * Logs information related to hotword detection service restarting.
+ */
+ public static void writeServiceRestartEvent(int detectorType, int reason) {
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED,
+ detectorType, reason);
+ }
+
+ /**
+ * Logs information related to keyphrase trigger.
+ */
+ public static void writeKeyphraseTriggerEvent(int detectorType, int result) {
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED,
+ detectorType, result);
+ }
+
+ /**
+ * Logs information related to hotword detector events.
+ */
+ public static void writeDetectorEvent(int detectorType, int event, int uid) {
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS,
+ detectorType, event, uid);
+ }
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0aaafef492bf..b6cacaf9f289 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1976,7 +1976,7 @@ public class SubscriptionManager {
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addSubscriptionInfoRecord(@NonNull String uniqueId, @Nullable String displayName,
- int slotIndex, int subscriptionType) {
+ int slotIndex, @SubscriptionType int subscriptionType) {
if (VDBG) {
logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", displayName:" + displayName + ", slotIndex:" + slotIndex
@@ -2012,7 +2012,8 @@ public class SubscriptionManager {
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void removeSubscriptionInfoRecord(@NonNull String uniqueId, int subscriptionType) {
+ public void removeSubscriptionInfoRecord(@NonNull String uniqueId,
+ @SubscriptionType int subscriptionType) {
if (VDBG) {
logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", subscriptionType: " + subscriptionType);
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ec734716f6e4..77d4837ccfb6 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -50,12 +50,13 @@ public class DataServiceCallback {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
- RESULT_ERROR_ILLEGAL_STATE})
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE,
+ RESULT_ERROR_INVALID_RESPONSE})
public @interface ResultCode {}
/** Request is completed successfully */
public static final int RESULT_SUCCESS = 0;
- /** Request is not support */
+ /** Request is not supported */
public static final int RESULT_ERROR_UNSUPPORTED = 1;
/** Request contains invalid arguments */
public static final int RESULT_ERROR_INVALID_ARG = 2;
@@ -68,6 +69,11 @@ public class DataServiceCallback {
* @hide
*/
public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
+ /**
+ * Request failed to complete due to an invalid response.
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_RESPONSE = 6;
private final IDataServiceCallback mCallback;
@@ -255,6 +261,8 @@ public class DataServiceCallback {
return "RESULT_ERROR_ILLEGAL_STATE";
case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
+ case RESULT_ERROR_INVALID_RESPONSE:
+ return "RESULT_ERROR_INVALID_RESPONSE";
default:
return "Unknown(" + resultCode + ")";
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index b905212a9100..546d2ce0e115 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -424,4 +424,24 @@ public class TelephonyIntents {
*/
@Deprecated
public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
+
+ /**
+ * <p>Broadcast sent to show Emergency notification due to Voice Over Wifi availability
+ *
+ * <p class="note">
+ * You can <em>not</em> receive this through components declared
+ * in manifests, only by explicitly registering for it with
+ * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+ * android.content.IntentFilter) Context.registerReceiver()}.
+ *
+ * <p class="note">
+ * Requires no permission.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide
+ */
+ public static final String ACTION_VOWIFI_ENABLED
+ = "com.android.internal.telephony.ACTION_VOWIFI_ENABLED";
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 71e576a2e5ac..7f4966375b98 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -44,7 +44,6 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 219749605)
class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index a9564fdd2332..e7a33543a86b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -46,6 +46,7 @@ import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,8 +62,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group2
-@FlakyTest(bugId = 219757170)
-class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
+open class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@@ -72,6 +72,11 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
WindowManagerConditionsFactory.isHomeActivityVisible()
))
+ @Before
+ open fun before() {
+ assumeFalse(isShellTransitionsEnabled)
+ }
+
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt
new file mode 100644
index 000000000000..7ffa51320487
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTests:ReOpenImeWindowTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group2
+@FlakyTest(bugId = 221854428)
+class ReOpenImeWindowTest_ShellTransit(private val testSpec: FlickerTestParameter)
+ : ReOpenImeWindowTest(testSpec) {
+ @Before
+ override fun before() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ }
+}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
index 18f962398fb8..bf8bd14645a0 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -18,11 +18,13 @@ package com.google.android.test.handwritingime;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.util.Log;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
+import android.widget.TextView;
import android.widget.Toast;
import java.util.Random;
@@ -79,6 +81,14 @@ public class HandwritingIme extends InputMethodService {
view.setPadding(0, 0, 0, 0);
view.addView(inner, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, height));
+ TextView text = new TextView(this);
+ text.setText("Handwriting IME");
+ text.setTextSize(13f);
+ text.setTextColor(getColor(android.R.color.white));
+ text.setGravity(Gravity.CENTER);
+ text.setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT, height));
+ view.addView(text);
inner.setBackgroundColor(0xff0110fe); // blue
return view;
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index 4ffdc9211f1d..87a5b900cc18 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -33,6 +33,7 @@ class InkView extends View {
private static final long FINISH_TIMEOUT = 2500;
private final HandwritingIme.HandwritingFinisher mHwCanceller;
private final HandwritingIme.StylusConsumer mConsumer;
+ private final int mTopInset;
private Paint mPaint;
private Path mPath;
private float mX, mY;
@@ -63,6 +64,7 @@ class InkView extends View {
setLayoutParams(new ViewGroup.LayoutParams(
metrics.getBounds().width() - insets.left - insets.right,
metrics.getBounds().height() - insets.top - insets.bottom));
+ mTopInset = insets.top;
}
@Override
@@ -74,12 +76,14 @@ class InkView extends View {
}
private void stylusStart(float x, float y) {
+ y = y - mTopInset;
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void stylusMove(float x, float y) {
+ y = y - mTopInset;
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (mPath.isEmpty()) {
diff --git a/tests/TrustTests/TEST_MAPPING b/tests/TrustTests/TEST_MAPPING
new file mode 100644
index 000000000000..b9c46bfbba8c
--- /dev/null
+++ b/tests/TrustTests/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 42715f9c3592..8d35eeec2a93 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1038,6 +1038,13 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou
continue;
}
+ if (maybe_name.value().substr(0, std::strlen("removed_")) == "removed_") {
+ // Skip resources that have been removed from the framework, but leave a hole so that
+ // other staged resources don't shift and break apps previously compiled against them
+ next_id.id++;
+ continue;
+ }
+
ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
.name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
.source = item_source,
diff --git a/tools/aapt2/tools/finalize_res.py b/tools/aapt2/tools/finalize_res.py
new file mode 100755
index 000000000000..0e4d865bc890
--- /dev/null
+++ b/tools/aapt2/tools/finalize_res.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python3
+# -*- coding: 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.
+#
+# 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.
+
+"""
+Finalize resource values in <staging-public-group> tags
+and convert those to <staging-public-group-final>
+
+Usage: $ANDROID_BUILD_TOP/frameworks/base/tools/aapt2/tools/finalize_res.py \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-staging.xml \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-final.xml
+"""
+
+import re
+import sys
+
+resTypes = ["attr", "id", "style", "string", "dimen", "color", "array", "drawable", "layout",
+ "anim", "animator", "interpolator", "mipmap", "integer", "transition", "raw", "bool",
+ "fraction"]
+
+_type_ids = {}
+_type = ""
+
+_lowest_staging_first_id = 0x01FFFFFF
+
+"""
+ Created finalized <public> declarations for staging resources, ignoring them if they've been
+ prefixed with removed_. The IDs are assigned without holes starting from the last ID for that
+ type currently finalized in public-final.xml.
+"""
+def finalize_item(raw):
+ name = raw.group(1)
+ if re.match(r'_*removed.+', name):
+ return ""
+ id = _type_ids[_type]
+ _type_ids[_type] += 1
+ return ' <public type="%s" name="%s" id="%s" />\n' % (_type, name, '0x{0:0{1}x}'.format(id, 8))
+
+
+"""
+ Finalizes staging-public-groups if they have any entries in them. Also keeps track of the
+ lowest first-id of the non-empty groups so that the next release's staging-public-groups can
+ be assigned the next down shifted first-id.
+"""
+def finalize_group(raw):
+ global _type, _lowest_staging_first_id
+ _type = raw.group(1)
+ id = int(raw.group(2), 16)
+ _type_ids[_type] = _type_ids.get(_type, id)
+ (res, count) = re.subn(' {0,4}<public name="(.+?)" */>\n', finalize_item, raw.group(3))
+ if count > 0:
+ res = raw.group(0).replace("staging-public-group",
+ "staging-public-group-final") + '\n' + res
+ _lowest_staging_first_id = min(id, _lowest_staging_first_id)
+ return res
+
+"""
+ Collects the max ID for each resType so that the new IDs can be assigned afterwards
+"""
+def collect_ids(raw):
+ for m in re.finditer(r'<public type="(.+?)" name=".+?" id="(.+?)" />', raw):
+ type = m.group(1)
+ id = int(m.group(2), 16)
+ _type_ids[type] = max(id + 1, _type_ids.get(type, 0))
+
+
+with open(sys.argv[1], "r+") as stagingFile:
+ with open(sys.argv[2], "r+") as finalFile:
+ existing = finalFile.read()
+ # Cut out the closing resources tag so that it can be concatenated easily later
+ existing = "\n".join(existing.rsplit("</resources>", 1))
+
+ # Collect the IDs from the existing already finalized resources
+ collect_ids(existing)
+
+ staging = stagingFile.read()
+ stagingSplit = staging.rsplit("<resources>")
+ staging = stagingSplit[1]
+ staging = re.sub(
+ r'<staging-public-group type="(.+?)" first-id="(.+?)">(.+?)</staging-public-group>',
+ finalize_group, staging, flags=re.DOTALL)
+ staging = re.sub(r' *\n', '\n', staging)
+ staging = re.sub(r'\n{3,}', '\n\n', staging)
+
+ # First write the existing finalized declarations and then append the new stuff
+ finalFile.seek(0)
+ finalFile.write(existing.strip("\n"))
+ finalFile.write("\n\n")
+ finalFile.write(staging.strip("\n"))
+ finalFile.write("\n")
+ finalFile.truncate()
+
+ stagingFile.seek(0)
+ # Include the documentation from public-staging.xml that was previously split out
+ stagingFile.write(stagingSplit[0])
+ # Write the next platform header
+ stagingFile.write("<resources>\n\n")
+ stagingFile.write(" <!-- ===============================================================\n")
+ stagingFile.write(" Resources added in version NEXT of the platform\n\n")
+ stagingFile.write(" NOTE: After this version of the platform is forked, changes cannot be made to the root\n")
+ stagingFile.write(" branch's groups for that release. Only merge changes to the forked platform branch.\n")
+ stagingFile.write(" =============================================================== -->\n")
+ stagingFile.write(" <eat-comment/>\n\n")
+
+ # Seed the next release's staging-public-groups as empty declarations,
+ # so its easy for another developer to expose a new public resource
+ nextId = _lowest_staging_first_id - 0x00010000
+ for resType in resTypes:
+ stagingFile.write(' <staging-public-group type="%s" first-id="%s">\n'
+ ' </staging-public-group>\n\n' %
+ (resType, '0x{0:0{1}x}'.format(nextId, 8)))
+ nextId -= 0x00010000
+
+ # Close the resources tag and truncate, since the file will be shorter than the previous
+ stagingFile.write("</resources>\n")
+ stagingFile.truncate()
diff --git a/tools/finalize_res/finalize_res.py b/tools/finalize_res/finalize_res.py
deleted file mode 100755
index aaf01875024e..000000000000
--- a/tools/finalize_res/finalize_res.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
-
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Finalize resource values in <staging-public-group> tags
-
-Usage: finalize_res.py core/res/res/values/public.xml public_finalized.xml
-"""
-
-import re, sys, codecs
-
-def finalize_item(raw):
- global _type, _id
- _id += 1
- return '<public type="%s" name="%s" id="%s" />' % (_type, raw.group(1), '0x{0:0{1}x}'.format(_id-1,8))
-
-def finalize_group(raw):
- global _type, _id
- _type = raw.group(1)
- _id = int(raw.group(2), 16)
- return re.sub(r'<public name="(.+?)" */>', finalize_item, raw.group(3))
-
-with open(sys.argv[1]) as f:
- raw = f.read()
- raw = re.sub(r'<staging-public-group type="(.+?)" first-id="(.+?)">(.+?)</staging-public-group>', finalize_group, raw, flags=re.DOTALL)
- with open(sys.argv[2], "w") as f:
- f.write(raw)
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 960447504d15..8630ec4b062d 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -963,6 +963,25 @@ public class WifiNl80211Manager {
}
/**
+ * Get the max number of SSIDs that the driver supports per scan.
+ *
+ * @param ifaceName Name of the interface.
+ */
+ public int getMaxNumScanSsids(@NonNull String ifaceName) {
+ IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+ if (scannerImpl == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);
+ return 0;
+ }
+ try {
+ return scannerImpl.getMaxNumScanSsids();
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to getMaxNumScanSsids");
+ }
+ return 0;
+ }
+
+ /**
* Return scan type for the parcelable {@link SingleScanSettings}
*/
private static int getScanType(@WifiAnnotations.ScanType int scanType) {