summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java263
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java19
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java19
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java43
-rw-r--r--apex/media/framework/Android.bp3
-rw-r--r--apex/media/framework/jarjar_rules.txt2
-rw-r--r--apex/media/framework/java/android/media/MediaCommunicationManager.java3
-rw-r--r--apex/media/framework/java/android/media/MediaSession2.java34
-rw-r--r--core/api/current.txt51
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/system-current.txt37
-rw-r--r--core/api/test-current.txt13
-rw-r--r--core/java/android/app/ActivityThread.java41
-rw-r--r--core/java/android/app/AppOpsManager.java391
-rw-r--r--core/java/android/app/Notification.java282
-rw-r--r--core/java/android/app/SystemServiceRegistry.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java1
-rw-r--r--core/java/android/app/backup/BackupAgent.java4
-rw-r--r--core/java/android/app/backup/FullBackup.java70
-rw-r--r--core/java/android/app/usage/UsageEvents.java11
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java2
-rw-r--r--core/java/android/companion/AssociationRequest.java4
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java12
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/content/pm/verify/domain/DomainOwner.java26
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationInfo.java7
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationManager.java374
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java202
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl (renamed from core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl)2
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationUserState.java (renamed from core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java)90
-rw-r--r--core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl6
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java131
-rw-r--r--core/java/android/hardware/biometrics/IAuthService.aidl12
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl6
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java21
-rw-r--r--core/java/android/net/Ikev2VpnProfile.java42
-rw-r--r--core/java/android/net/IpSecAlgorithm.java9
-rw-r--r--core/java/android/net/OemNetworkPreferences.java19
-rw-r--r--core/java/android/net/vcn/IVcnStatusCallback.aidl1
-rw-r--r--core/java/android/net/vcn/VcnManager.java54
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/CertUtils.java46
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java73
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java276
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java143
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java87
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java96
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java285
-rwxr-xr-xcore/java/android/os/Build.java8
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java4
-rw-r--r--core/java/android/os/VibrationEffect.java18
-rw-r--r--core/java/android/os/VibratorInfo.java43
-rw-r--r--core/java/android/os/storage/StorageManager.java10
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java7
-rw-r--r--core/java/android/permission/PermissionControllerManager.java2
-rw-r--r--core/java/android/provider/DeviceConfig.java16
-rw-r--r--core/java/android/service/storage/ExternalStorageService.java7
-rw-r--r--core/java/android/service/storage/IExternalStorageService.aidl3
-rw-r--r--core/java/android/view/InputEventAssigner.java92
-rw-r--r--core/java/android/view/MotionEvent.java1
-rw-r--r--core/java/android/view/NotificationHeaderView.java11
-rw-r--r--core/java/android/view/ViewConfiguration.java39
-rw-r--r--core/java/android/view/ViewFrameInfo.java39
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl7
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl31
-rw-r--r--core/java/android/view/displayhash/DisplayHashResultCallback.java9
-rw-r--r--core/java/android/view/textclassifier/TextClassificationConstants.java14
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java26
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl6
-rw-r--r--core/java/com/android/internal/infra/GlobalWhitelistState.java12
-rw-r--r--core/java/com/android/internal/infra/WhitelistHelper.java9
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java3
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java2
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java36
-rw-r--r--core/java/com/android/internal/widget/NotificationExpandButton.java147
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp123
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/color/text_color_primary_device_default_dark.xml23
-rw-r--r--core/res/res/color/text_color_primary_device_default_light.xml23
-rw-r--r--core/res/res/color/text_color_secondary_device_default_dark.xml23
-rw-r--r--core/res/res/color/text_color_secondary_device_default_light.xml23
-rw-r--r--core/res/res/color/text_color_tertiary_device_default_dark.xml23
-rw-r--r--core/res/res/color/text_color_tertiary_device_default_light.xml23
-rw-r--r--core/res/res/drawable/expand_button_pill_bg.xml (renamed from core/res/res/drawable/conversation_unread_bg.xml)2
-rw-r--r--core/res/res/drawable/ic_collapse_notification.xml5
-rw-r--r--core/res/res/drawable/ic_expand_notification.xml5
-rw-r--r--core/res/res/layout/notification_expand_button.xml56
-rw-r--r--core/res/res/layout/notification_template_header.xml28
-rw-r--r--core/res/res/layout/notification_template_material_base.xml10
-rw-r--r--core/res/res/layout/notification_template_material_call.xml11
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml27
-rw-r--r--core/res/res/values/attrs_manifest.xml2
-rw-r--r--core/res/res/values/colors_device_defaults.xml7
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/dimens.xml30
-rw-r--r--core/res/res/values/strings.xml26
-rw-r--r--core/res/res/values/symbols.xml25
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java3
-rw-r--r--core/tests/coretests/src/android/app/usage/OWNERS2
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java18
-rw-r--r--core/tests/coretests/src/android/graphics/FontListParserTest.java109
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java29
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/platform.xml2
-rw-r--r--data/etc/services.core.protolog.json30
-rw-r--r--graphics/java/android/graphics/FontListParser.java14
-rw-r--r--keystore/java/android/security/LegacyVpnProfileStore.java142
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java7
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/FrameInfo.h4
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp3
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl4
-rw-r--r--media/jni/android_media_MediaDrm.cpp7
-rw-r--r--packages/Connectivity/framework/api/system-current.txt2
-rw-r--r--packages/Connectivity/framework/src/android/net/CaptivePortal.java7
-rw-r--r--packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl1
-rw-r--r--packages/LocalTransport/OWNERS1
-rw-r--r--packages/LocalTransport/src/com/android/localtransport/LocalTransport.java3
-rw-r--r--packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java7
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml36
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java20
-rw-r--r--packages/SettingsLib/res/values/strings.xml11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java50
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java4
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_enroll.xml34
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view.xml)5
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml22
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml11
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java183
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java211
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java42
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java41
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java13
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java98
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java24
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java148
-rw-r--r--services/core/java/com/android/server/ConnectivityServiceInitializer.java9
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java60
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java66
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java45
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java25
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java9
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java8
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java30
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java1
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java30
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java616
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java165
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java169
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java80
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java10
-rw-r--r--services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java12
-rw-r--r--services/core/java/com/android/server/connectivity/QosCallbackTracker.java26
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java215
-rw-r--r--services/core/java/com/android/server/connectivity/VpnProfileStore.java77
-rw-r--r--services/core/java/com/android/server/display/DeviceStateToLayoutMap.java86
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java10
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java35
-rw-r--r--services/core/java/com/android/server/display/layout/Layout.java20
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java4
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java9
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java15
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java15
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java39
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java22
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java18
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java99
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java17
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java16
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java (renamed from services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java)35
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java77
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java41
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java4
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java6
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java44
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java4
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java81
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java14
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java3
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java23
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java216
-rw-r--r--services/core/java/com/android/server/wm/Transition.java20
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java50
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java113
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java99
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java28
-rw-r--r--services/core/xsd/Android.bp7
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd35
-rw-r--r--services/core/xsd/display-layout-config/OWNERS3
-rw-r--r--services/core/xsd/display-layout-config/display-layout-config.xsd57
-rw-r--r--services/core/xsd/display-layout-config/schema/current.txt34
-rw-r--r--services/core/xsd/display-layout-config/schema/last_current.txt0
-rw-r--r--services/core/xsd/display-layout-config/schema/last_removed.txt0
-rw-r--r--services/core/xsd/display-layout-config/schema/removed.txt1
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java43
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java32
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt19
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt121
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt12
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt8
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt14
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt (renamed from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt)44
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java29
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/job/JobStoreTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java73
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java8
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java6
-rw-r--r--telephony/java/android/telephony/ims/RcsContactPresenceTuple.java62
-rw-r--r--telephony/java/android/telephony/ims/RcsContactUceCapability.java1
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java102
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java40
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java1
-rw-r--r--tests/Input/src/com/android/test/input/InputEventAssignerTest.kt130
-rw-r--r--tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt11
-rw-r--r--tests/net/common/java/android/net/CaptivePortalTest.java15
-rw-r--r--tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt10
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java140
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java101
-rw-r--r--tests/vcn/assets/self-signed-ca.pem20
-rw-r--r--tests/vcn/java/android/net/vcn/VcnManagerTest.java3
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java113
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java87
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java78
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java117
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java134
-rw-r--r--tools/bit/make.cpp15
299 files changed, 9083 insertions, 2743 deletions
diff --git a/Android.bp b/Android.bp
index 20ca1b7c1020..9374c01066e6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -581,9 +581,11 @@ java_library {
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
+ "android.hardware.vibrator-V2-java",
"android.security.apc-java",
"android.security.authorization-java",
"android.security.usermanager-java",
+ "android.security.vpnprofilestore-java",
"android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index e83c64c37678..5a4596108aa5 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -44,7 +44,7 @@ import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
public class TypefaceCreatePerfTest {
// A font file name in asset directory.
- private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
+ private static final String TEST_FONT_NAME = "DancingScript.ttf";
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
index 34ba753b3daa..862d8b7cac50 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
@@ -25,7 +25,9 @@ import com.android.server.job.controllers.JobStatus;
public interface JobCompletedListener {
/**
* Callback for when a job is completed.
+ *
+ * @param stopReason The stop reason provided to JobParameters.
* @param needsReschedule Whether the implementing class should reschedule this job.
*/
- void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule);
+ void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index e8e2c27f1554..0308d68d6a56 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -73,20 +73,48 @@ class JobConcurrencyManager {
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
- // Try to give higher priority types lower values.
+ /**
+ * Set of possible execution types that a job can have. The actual type(s) of a job are based
+ * on the {@link JobStatus#lastEvaluatedPriority}, which is typically evaluated right before
+ * execution (when we're trying to determine which jobs to run next) and won't change after the
+ * job has started executing.
+ *
+ * Try to give higher priority types lower values.
+ *
+ * @see #getJobWorkTypes(JobStatus)
+ */
+
+ /** Job shouldn't run or qualify as any other work type. */
static final int WORK_TYPE_NONE = 0;
+ /** The job is for an app in the TOP state for a currently active user. */
static final int WORK_TYPE_TOP = 1 << 0;
- static final int WORK_TYPE_EJ = 1 << 1;
- static final int WORK_TYPE_BG = 1 << 2;
- static final int WORK_TYPE_BGUSER = 1 << 3;
+ /**
+ * The job is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher
+ * state (excluding {@link ActivityManager#PROCESS_STATE_TOP} for a currently active user.
+ */
+ static final int WORK_TYPE_FGS = 1 << 1;
+ /** The job is allowed to run as an expedited job for a currently active user. */
+ static final int WORK_TYPE_EJ = 1 << 2;
+ /**
+ * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP},
+ * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a currently active user, so
+ * can run as a background job.
+ */
+ static final int WORK_TYPE_BG = 1 << 3;
+ /**
+ * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP},
+ * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a completely background user,
+ * so can run as a background user job.
+ */
+ static final int WORK_TYPE_BGUSER = 1 << 4;
@VisibleForTesting
- static final int NUM_WORK_TYPES = 4;
- private static final int ALL_WORK_TYPES =
- WORK_TYPE_TOP | WORK_TYPE_EJ | WORK_TYPE_BG | WORK_TYPE_BGUSER;
+ static final int NUM_WORK_TYPES = 5;
+ private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
WORK_TYPE_NONE,
WORK_TYPE_TOP,
+ WORK_TYPE_FGS,
WORK_TYPE_EJ,
WORK_TYPE_BG,
WORK_TYPE_BGUSER
@@ -95,12 +123,15 @@ class JobConcurrencyManager {
public @interface WorkType {
}
- private static String workTypeToString(@WorkType int workType) {
+ @VisibleForTesting
+ static String workTypeToString(@WorkType int workType) {
switch (workType) {
case WORK_TYPE_NONE:
return "NONE";
case WORK_TYPE_TOP:
return "TOP";
+ case WORK_TYPE_FGS:
+ return "FGS";
case WORK_TYPE_EJ:
return "EJ";
case WORK_TYPE_BG:
@@ -131,58 +162,60 @@ class JobConcurrencyManager {
new WorkConfigLimitsPerMemoryTrimLevel(
new WorkTypeConfig("screen_on_normal", 11,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
new WorkTypeConfig("screen_on_low", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
- Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
- new WorkTypeConfig("screen_on_critical", 5,
+ new WorkTypeConfig("screen_on_critical", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
);
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_off_normal", 13,
+ new WorkTypeConfig("screen_off_normal", 15,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
- new WorkTypeConfig("screen_off_moderate", 13,
+ new WorkTypeConfig("screen_off_moderate", 15,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
- new WorkTypeConfig("screen_off_low", 7,
+ new WorkTypeConfig("screen_off_low", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
- Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
- new WorkTypeConfig("screen_off_critical", 5,
+ new WorkTypeConfig("screen_off_critical", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
@@ -977,10 +1010,11 @@ class JobConcurrencyManager {
int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0;
- // TODO: create dedicated work type for FGS
if (shouldRunAsFgUserJob(js)) {
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
classification |= WORK_TYPE_TOP;
+ } else if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE) {
+ classification |= WORK_TYPE_FGS;
} else {
classification |= WORK_TYPE_BG;
}
@@ -1001,11 +1035,13 @@ class JobConcurrencyManager {
private static final String KEY_PREFIX_MAX_TOTAL =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+ private static final String KEY_PREFIX_MAX_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "max_fgs_";
private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
private static final String KEY_PREFIX_MAX_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+ private static final String KEY_PREFIX_MIN_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "min_fgs_";
private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
private static final String KEY_PREFIX_MIN_BGUSER =
@@ -1053,6 +1089,10 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+ final int maxFgs = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_FGS + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_FGS, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_FGS, maxFgs);
final int maxEj = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
@@ -1074,6 +1114,12 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
remaining -= minTop;
+ // Ensure fgs is in the range [0, min(maxFgs, remaining)]
+ final int minFgs = Math.max(0, Math.min(Math.min(maxFgs, remaining),
+ properties.getInt(KEY_PREFIX_MIN_FGS + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_FGS))));
+ mMinReservedSlots.put(WORK_TYPE_FGS, minFgs);
+ remaining -= minFgs;
// Ensure ej is in the range [0, min(maxEj, remaining)]
final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
@@ -1111,6 +1157,10 @@ class JobConcurrencyManager {
.println();
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
.println();
+ pw.print(KEY_PREFIX_MIN_FGS + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_FGS))
+ .println();
+ pw.print(KEY_PREFIX_MAX_FGS + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_FGS))
+ .println();
pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
.println();
pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
@@ -1205,6 +1255,7 @@ class JobConcurrencyManager {
private int mConfigMaxTotal;
private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mRecycledReserved = new SparseIntArray(NUM_WORK_TYPES);
/**
* Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't
@@ -1220,11 +1271,14 @@ class JobConcurrencyManager {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+ mConfigNumReservedSlots.put(WORK_TYPE_FGS,
+ workTypeConfig.getMinReserved(WORK_TYPE_FGS));
mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_FGS, workTypeConfig.getMax(WORK_TYPE_FGS));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
@@ -1260,15 +1314,10 @@ class JobConcurrencyManager {
// We don't need to adjust reservations if only one work type was modified
// because that work type is the one we're using.
- // 0 is WORK_TYPE_NONE.
- int workType = 1;
- int rem = workTypes;
- while (rem > 0) {
- if ((rem & 1) != 0) {
+ for (int workType = 1; workType <= workTypes; workType <<= 1) {
+ if ((workType & workTypes) == workType) {
maybeAdjustReservations(workType);
}
- rem = rem >>> 1;
- workType = workType << 1;
}
}
}
@@ -1280,21 +1329,11 @@ class JobConcurrencyManager {
int numAdj = 0;
// We don't know which type we'll classify the job as when we run it yet, so make sure
// we have space in all applicable slots.
- if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
- mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + adj);
- numAdj++;
- }
- if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
- mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + adj);
- numAdj++;
- }
- if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
- mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + adj);
- numAdj++;
- }
- if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
- mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + adj);
- numAdj++;
+ for (int workType = 1; workType <= workTypes; workType <<= 1) {
+ if ((workTypes & workType) == workType) {
+ mNumPendingJobs.put(workType, mNumPendingJobs.get(workType) + adj);
+ numAdj++;
+ }
}
return numAdj;
@@ -1388,105 +1427,45 @@ class JobConcurrencyManager {
mNumUnspecializedRemaining = mConfigMaxTotal;
// Step 1
- int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
- int resTop = runTop;
- mNumUnspecializedRemaining -= resTop;
- int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
- int resEj = runEj;
- mNumUnspecializedRemaining -= resEj;
- int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
- int resBg = runBg;
- mNumUnspecializedRemaining -= resBg;
- int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
- int resBgUser = runBgUser;
- mNumUnspecializedRemaining -= resBgUser;
+ for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
+ int run = mNumRunningJobs.get(workType);
+ mRecycledReserved.put(workType, run);
+ mNumUnspecializedRemaining -= run;
+ }
// Step 2
- final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
- int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
- Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
- resTop += fillUp;
- mNumUnspecializedRemaining -= fillUp;
- final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
- fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
- Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
- resEj += fillUp;
- mNumUnspecializedRemaining -= fillUp;
- final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
- fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
- Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
- resBg += fillUp;
- mNumUnspecializedRemaining -= fillUp;
- final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
- fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
- Math.min(numBgUser,
- mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
- resBgUser += fillUp;
- mNumUnspecializedRemaining -= fillUp;
+ for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
+ int num = mNumRunningJobs.get(workType) + mNumPendingJobs.get(workType);
+ int res = mRecycledReserved.get(workType);
+ int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(num, mConfigNumReservedSlots.get(workType) - res)));
+ res += fillUp;
+ mRecycledReserved.put(workType, res);
+ mNumUnspecializedRemaining -= fillUp;
+ }
// Step 3
- int unspecializedAssigned = Math.max(0,
- Math.min(mNumUnspecializedRemaining,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
- mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
- mNumUnspecializedRemaining -= unspecializedAssigned;
-
- unspecializedAssigned = Math.max(0,
- Math.min(mNumUnspecializedRemaining,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
- mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
- mNumUnspecializedRemaining -= unspecializedAssigned;
-
- unspecializedAssigned = Math.max(0,
- Math.min(mNumUnspecializedRemaining,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
- mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
- mNumUnspecializedRemaining -= unspecializedAssigned;
-
- unspecializedAssigned = Math.max(0,
- Math.min(mNumUnspecializedRemaining,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
- - resBgUser));
- mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
- mNumUnspecializedRemaining -= unspecializedAssigned;
+ for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
+ int num = mNumRunningJobs.get(workType) + mNumPendingJobs.get(workType);
+ int res = mRecycledReserved.get(workType);
+ int unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(workType), num) - res));
+ mNumActuallyReservedSlots.put(workType, res + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
+ }
}
int canJobStart(int workTypes) {
- if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
- final int maxAllowed = Math.min(
- mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
- mNumActuallyReservedSlots.get(WORK_TYPE_TOP) + mNumUnspecializedRemaining);
- if (mNumRunningJobs.get(WORK_TYPE_TOP) + mNumStartingJobs.get(WORK_TYPE_TOP)
- < maxAllowed) {
- return WORK_TYPE_TOP;
- }
- }
- if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
- final int maxAllowed = Math.min(
- mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
- mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
- if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
- < maxAllowed) {
- return WORK_TYPE_EJ;
- }
- }
- if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
- final int maxAllowed = Math.min(
- mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
- mNumActuallyReservedSlots.get(WORK_TYPE_BG) + mNumUnspecializedRemaining);
- if (mNumRunningJobs.get(WORK_TYPE_BG) + mNumStartingJobs.get(WORK_TYPE_BG)
- < maxAllowed) {
- return WORK_TYPE_BG;
- }
- }
- if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
- final int maxAllowed = Math.min(
- mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
- mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
- + mNumUnspecializedRemaining);
- if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
- < maxAllowed) {
- return WORK_TYPE_BGUSER;
+ for (int workType = 1; workType <= workTypes; workType <<= 1) {
+ if ((workTypes & workType) == workType) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(workType),
+ mNumActuallyReservedSlots.get(workType) + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(workType) + mNumStartingJobs.get(workType)
+ < maxAllowed) {
+ return workType;
+ }
}
}
return WORK_TYPE_NONE;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 8bb03e911528..515cb747a99e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1745,11 +1745,12 @@ public class JobSchedulerService extends com.android.server.SystemService
* A job just finished executing. We fetch the
* {@link com.android.server.job.controllers.JobStatus} from the store and depending on
* whether we want to reschedule we re-add it to the controllers.
- * @param jobStatus Completed job.
+ *
+ * @param jobStatus Completed job.
* @param needsReschedule Whether the implementing class should reschedule this job.
*/
@Override
- public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
+ public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) {
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
@@ -1767,6 +1768,11 @@ public class JobSchedulerService extends com.android.server.SystemService
// we stop it.
final JobStatus rescheduledJob = needsReschedule
? getRescheduleJobForFailureLocked(jobStatus) : null;
+ if (rescheduledJob != null
+ && (stopReason == JobParameters.REASON_TIMEOUT
+ || stopReason == JobParameters.REASON_PREEMPT)) {
+ rescheduledJob.disallowRunInBatterySaverAndDoze();
+ }
// Do not write back immediately if this is a periodic job. The job may get lost if system
// shuts down before it is added back.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 2a23d60d8af6..96734499ee20 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -273,10 +273,12 @@ public final class JobServiceContext implements ServiceConnection {
// another binding flag for that.
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALMOST_PERCEPTIBLE
- | Context.BIND_ALLOW_NETWORK_ACCESS;
+ | Context.BIND_ALLOW_NETWORK_ACCESS
+ | Context.BIND_NOT_APP_COMPONENT_USAGE;
} else {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_NOT_PERCEPTIBLE;
+ | Context.BIND_NOT_PERCEPTIBLE
+ | Context.BIND_NOT_APP_COMPONENT_USAGE;
}
binding = mContext.bindServiceAsUser(intent, this, bindFlags,
UserHandle.of(job.getUserId()));
@@ -379,8 +381,8 @@ public final class JobServiceContext implements ServiceConnection {
}
boolean isWithinExecutionGuaranteeTime() {
- return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis
- < sElapsedRealtimeClock.millis();
+ return sElapsedRealtimeClock.millis()
+ < mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
}
@GuardedBy("mLock")
@@ -848,11 +850,12 @@ public final class JobServiceContext implements ServiceConnection {
}
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
- mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
+ final int stopReason = mParams.getStopReason();
+ mJobPackageTracker.noteInactive(completedJob, stopReason, reason);
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
completedJob.getSourceUid(), null, completedJob.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
- mParams.getStopReason(), completedJob.getStandbyBucket(), completedJob.getJobId(),
+ stopReason, completedJob.getStandbyBucket(), completedJob.getJobId(),
completedJob.hasChargingConstraint(),
completedJob.hasBatteryNotLowConstraint(),
completedJob.hasStorageNotLowConstraint(),
@@ -863,7 +866,7 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.hasContentTriggerConstraint());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
- mParams.getStopReason());
+ stopReason);
} catch (RemoteException e) {
// Whatever.
}
@@ -882,7 +885,7 @@ public final class JobServiceContext implements ServiceConnection {
service = null;
mAvailable = true;
removeOpTimeOutLocked();
- mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+ mCompletedListener.onJobCompletedLocked(completedJob, stopReason, reschedule);
mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index eaf8f4d96331..aa8d98c01853 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -954,7 +954,7 @@ public final class JobStore {
appBucket, sourceTag,
elapsedRuntimes.first, elapsedRuntimes.second,
lastSuccessfulRunTime, lastFailedRunTime,
- (rtcIsGood) ? null : rtcRuntimes, internalFlags);
+ (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0);
return js;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 192f5e66255d..79ef321eaf07 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -64,7 +64,7 @@ public final class DeviceIdleJobsController extends StateController {
* when the app is temp whitelisted or in the foreground.
*/
private final ArraySet<JobStatus> mAllowInIdleJobs;
- private final SparseBooleanArray mForegroundUids;
+ private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
private final DeviceIdleJobsDelayHandler mHandler;
private final PowerManager mPowerManager;
@@ -77,7 +77,6 @@ public final class DeviceIdleJobsController extends StateController {
private int[] mDeviceIdleWhitelistAppIds;
private int[] mPowerSaveTempWhitelistAppIds;
- // onReceive
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -120,6 +119,10 @@ public final class DeviceIdleJobsController extends StateController {
}
};
+ /** Criteria for whether or not we should a job's rush evaluation when the device exits Doze. */
+ private final Predicate<JobStatus> mShouldRushEvaluation = (jobStatus) ->
+ jobStatus.isRequestedExpeditedJob() || mForegroundUids.get(jobStatus.getSourceUid());
+
public DeviceIdleJobsController(JobSchedulerService service) {
super(service);
@@ -133,7 +136,6 @@ public final class DeviceIdleJobsController extends StateController {
mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor();
mAllowInIdleJobs = new ArraySet<>();
- mForegroundUids = new SparseBooleanArray();
final IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
@@ -156,14 +158,9 @@ public final class DeviceIdleJobsController extends StateController {
mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
} else {
- // When coming out of doze, process all foreground uids immediately, while others
- // will be processed after a delay of 3 seconds.
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) {
- mService.getJobStore().forEachJobForSourceUid(
- mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor);
- }
- }
+ // When coming out of doze, process all foreground uids and EJs immediately,
+ // while others will be processed after a delay of 3 seconds.
+ mService.getJobStore().forEachJob(mShouldRushEvaluation, mDeviceIdleUpdateFunctor);
mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 5bdeb38a1424..bad8dc1ad1cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -91,6 +91,12 @@ public final class JobStatus {
static final int CONSTRAINT_WITHIN_EXPEDITED_QUOTA = 1 << 23; // Implicit constraint
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
+ // The following set of dynamic constraints are for specific use cases (as explained in their
+ // relative naming and comments). Right now, they apply different constraints, which is fine,
+ // but if in the future, we have overlapping dynamic constraint sets, removing one constraint
+ // set may accidentally remove a constraint applied by another dynamic set.
+ // TODO: properly handle overlapping dynamic constraint sets
+
/**
* The additional set of dynamic constraints that must be met if the job's effective bucket is
* {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
@@ -103,6 +109,13 @@ public final class JobStatus {
| CONSTRAINT_IDLE;
/**
+ * The additional set of dynamic constraints that must be met if this is an expedited job that
+ * had a long enough run while the device was Dozing or in battery saver.
+ */
+ private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS =
+ CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
+
+ /**
* Standard media URIs that contain the media files that might be important to the user.
* @see #mHasMediaBackupExemption
*/
@@ -426,7 +439,8 @@ public final class JobStatus {
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, int standbyBucket, String tag, int numFailures,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
- long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
+ long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
+ int dynamicConstraints) {
this.job = job;
this.callingUid = callingUid;
this.standbyBucket = standbyBucket;
@@ -487,6 +501,7 @@ public final class JobStatus {
}
this.requiredConstraints = requiredConstraints;
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+ addDynamicConstraints(dynamicConstraints);
mReadyNotDozing = canRunInDoze();
if (standbyBucket == RESTRICTED_INDEX) {
addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
@@ -521,7 +536,7 @@ public final class JobStatus {
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
- jobStatus.getInternalFlags());
+ jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
if (jobStatus.mPersistedUtcTimes != null) {
if (DEBUG) {
@@ -543,12 +558,12 @@ public final class JobStatus {
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
long lastSuccessfulRunTime, long lastFailedRunTime,
Pair<Long, Long> persistedExecutionTimesUTC,
- int innerFlags) {
+ int innerFlags, int dynamicConstraints) {
this(job, callingUid, sourcePkgName, sourceUserId,
standbyBucket,
sourceTag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
+ lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
// Only during initial inflation do we record the UTC-timebase execution bounds
// read from the persistent store. If we ever have to recreate the JobStatus on
@@ -572,7 +587,8 @@ public final class JobStatus {
rescheduling.getStandbyBucket(),
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
newLatestRuntimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
+ lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
+ rescheduling.mDynamicConstraints);
}
/**
@@ -609,7 +625,7 @@ public final class JobStatus {
standbyBucket, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
- /*innerFlags=*/ 0);
+ /*innerFlags=*/ 0, /* dynamicConstraints */ 0);
}
public void enqueueWorkLocked(JobWorkItem work) {
@@ -1083,12 +1099,15 @@ public final class JobStatus {
* in Doze.
*/
public boolean canRunInDoze() {
- return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsExpeditedJob();
+ return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
+ || (shouldTreatAsExpeditedJob()
+ && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
}
boolean canRunInBatterySaver() {
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
- || shouldTreatAsExpeditedJob();
+ || (shouldTreatAsExpeditedJob()
+ && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
}
boolean shouldIgnoreNetworkBlocking() {
@@ -1245,6 +1264,14 @@ public final class JobStatus {
}
/**
+ * Add additional constraints to prevent this job from running when doze or battery saver are
+ * active.
+ */
+ public void disallowRunInBatterySaverAndDoze() {
+ addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS);
+ }
+
+ /**
* Indicates that this job cannot run without the specified constraints. This is evaluated
* separately from the job's explicitly requested constraints and MUST be satisfied before
* the job can run if the app doesn't have quota.
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index b18a22b408f5..1d6f20dd4b27 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -47,6 +47,7 @@ java_library {
static_libs: [
"exoplayer2-extractor",
"mediatranscoding_aidl_interface-java",
+ "modules-annotation-minsdk",
"modules-utils-build",
],
jarjar_rules: "jarjar_rules.txt",
@@ -108,7 +109,7 @@ filegroup {
filegroup {
name: "mediaparser-srcs",
srcs: [
- "java/android/media/MediaParser.java"
+ "java/android/media/MediaParser.java",
],
path: "java",
}
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
index eb71fddc05cb..91489dcee0a1 100644
--- a/apex/media/framework/jarjar_rules.txt
+++ b/apex/media/framework/jarjar_rules.txt
@@ -1,2 +1,2 @@
-rule com.android.modules.utils.** android.media.internal.utils.@1
+rule com.android.modules.** android.media.internal.@1
rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index 9ec25fe48a2e..f39bcfb267bf 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -27,12 +27,14 @@ import android.annotation.SystemService;
import android.content.Context;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
+import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.modules.annotation.MinSdk;
import com.android.modules.utils.build.SdkLevel;
import java.util.Collections;
@@ -45,6 +47,7 @@ import java.util.concurrent.Executor;
* Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
* that applications have published to express their ongoing media playback state.
*/
+@MinSdk(Build.VERSION_CODES.S)
@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
public class MediaCommunicationManager {
private static final String TAG = "MediaCommunicationManager";
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
index 6397ba3996f3..7697359e7caf 100644
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ b/apex/media/framework/java/android/media/MediaSession2.java
@@ -32,6 +32,7 @@ import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.media.session.MediaSessionManager;
import android.media.session.MediaSessionManager.RemoteUserInfo;
import android.os.BadParcelableException;
import android.os.Bundle;
@@ -43,6 +44,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import com.android.modules.utils.build.SdkLevel;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -86,6 +89,7 @@ public class MediaSession2 implements AutoCloseable {
private final String mSessionId;
private final PendingIntent mSessionActivity;
private final Session2Token mSessionToken;
+ private final MediaSessionManager mMediaSessionManager;
private final MediaCommunicationManager mCommunicationManager;
private final Handler mResultHandler;
@@ -114,7 +118,13 @@ public class MediaSession2 implements AutoCloseable {
mSessionStub = new Session2Link(this);
mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
mSessionStub, tokenExtras);
- mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
+ if (SdkLevel.isAtLeastS()) {
+ mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
+ mMediaSessionManager = null;
+ } else {
+ mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ mCommunicationManager = null;
+ }
// NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
mResultHandler = new Handler(context.getMainLooper());
mClosed = false;
@@ -315,6 +325,14 @@ public class MediaSession2 implements AutoCloseable {
return mCallback;
}
+ boolean isTrustedForMediaControl(RemoteUserInfo remoteUserInfo) {
+ if (SdkLevel.isAtLeastS()) {
+ return mCommunicationManager.isTrustedForMediaControl(remoteUserInfo);
+ } else {
+ return mMediaSessionManager.isTrustedForMediaControl(remoteUserInfo);
+ }
+ }
+
void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
synchronized (mLock) {
if (mForegroundServiceEventCallback == callback) {
@@ -350,7 +368,7 @@ public class MediaSession2 implements AutoCloseable {
final ControllerInfo controllerInfo = new ControllerInfo(
remoteUserInfo,
- mCommunicationManager.isTrustedForMediaControl(remoteUserInfo),
+ isTrustedForMediaControl(remoteUserInfo),
controller,
connectionHints);
mCallbackExecutor.execute(() -> {
@@ -606,9 +624,15 @@ public class MediaSession2 implements AutoCloseable {
// Notify framework about the newly create session after the constructor is finished.
// Otherwise, framework may access the session before the initialization is finished.
try {
- MediaCommunicationManager manager =
- mContext.getSystemService(MediaCommunicationManager.class);
- manager.notifySession2Created(session2.getToken());
+ if (SdkLevel.isAtLeastS()) {
+ MediaCommunicationManager manager =
+ mContext.getSystemService(MediaCommunicationManager.class);
+ manager.notifySession2Created(session2.getToken());
+ } else {
+ MediaSessionManager manager =
+ mContext.getSystemService(MediaSessionManager.class);
+ manager.notifySession2Created(session2.getToken());
+ }
} catch (Exception e) {
session2.close();
throw e;
diff --git a/core/api/current.txt b/core/api/current.txt
index 8710e613602f..a06f9943bde2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11768,6 +11768,7 @@ package android.content.pm {
method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
method public CharSequence loadDescription(android.content.pm.PackageManager);
+ field public static final int CATEGORY_ACCESSIBILITY = 8; // 0x8
field public static final int CATEGORY_AUDIO = 1; // 0x1
field public static final int CATEGORY_GAME = 0; // 0x0
field public static final int CATEGORY_IMAGE = 3; // 0x3
@@ -12947,6 +12948,27 @@ package android.content.pm {
}
+package android.content.pm.verify.domain {
+
+ public final class DomainVerificationManager {
+ method @Nullable public android.content.pm.verify.domain.DomainVerificationUserState getDomainVerificationUserState(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ }
+
+ public final class DomainVerificationUserState implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+ method @NonNull public String getPackageName();
+ method @NonNull public android.os.UserHandle getUser();
+ method @NonNull public boolean isLinkHandlingAllowed();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserState> CREATOR;
+ field public static final int DOMAIN_STATE_NONE = 0; // 0x0
+ field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
+ field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -17535,6 +17557,9 @@ package android.hardware.biometrics {
public class BiometricManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getButtonLabel(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getPromptMessage(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getSettingName(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
@@ -26723,7 +26748,22 @@ package android.net.vcn {
public class VcnManager {
method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
+ method public void registerVcnStatusCallback(@NonNull android.os.ParcelUuid, @NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnStatusCallback);
method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
+ method public void unregisterVcnStatusCallback(@NonNull android.net.vcn.VcnManager.VcnStatusCallback);
+ field public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; // 0x1
+ field public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; // 0x0
+ field public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_ACTIVE = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_INACTIVE = 1; // 0x1
+ field public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; // 0x0
+ field public static final int VCN_STATUS_CODE_SAFE_MODE = 3; // 0x3
+ }
+
+ public abstract static class VcnManager.VcnStatusCallback {
+ ctor public VcnManager.VcnStatusCallback();
+ method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
+ method public abstract void onVcnStatusChanged(int);
}
}
@@ -30273,6 +30313,7 @@ package android.os {
field public static final String HARDWARE;
field public static final String HOST;
field public static final String ID;
+ field public static final boolean IS_DEBUGGABLE;
field public static final String MANUFACTURER;
field public static final String MODEL;
field @NonNull public static final String ODM_SKU;
@@ -30431,19 +30472,10 @@ package android.os {
public abstract class CombinedVibrationEffect implements android.os.Parcelable {
method @NonNull public static android.os.CombinedVibrationEffect createSynced(@NonNull android.os.VibrationEffect);
method public int describeContents();
- method @NonNull public static android.os.CombinedVibrationEffect.SequentialCombination startSequential();
method @NonNull public static android.os.CombinedVibrationEffect.SyncedCombination startSynced();
field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect> CREATOR;
}
- public static final class CombinedVibrationEffect.SequentialCombination {
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect, int);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect, int);
- method @NonNull public android.os.CombinedVibrationEffect combine();
- }
-
public static final class CombinedVibrationEffect.SyncedCombination {
method @NonNull public android.os.CombinedVibrationEffect.SyncedCombination addVibrator(int, @NonNull android.os.VibrationEffect);
method @NonNull public android.os.CombinedVibrationEffect combine();
@@ -50983,6 +51015,7 @@ package android.view.displayhash {
method public void onDisplayHashError(int);
method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash);
field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe
+ field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 91ccefee5d54..d6786f8a3f8e 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -241,8 +241,8 @@ package android.os {
package android.os.storage {
public class StorageManager {
- method public void notifyAppIoBlocked(@NonNull String, int, int, int);
- method public void notifyAppIoResumed(@NonNull String, int, int, int);
+ method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
+ method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 0; // 0x0
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 019e7ffe11ef..f01724316c5f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -154,6 +154,7 @@ package android {
field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
+ field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
@@ -423,6 +424,9 @@ package android.app {
method @Nullable public static String opToPermission(@NonNull String);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
+ field public static final int HISTORY_FLAGS_ALL = 3; // 0x3
+ field public static final int HISTORY_FLAG_AGGREGATE = 1; // 0x1
+ field public static final int HISTORY_FLAG_DISCRETE = 2; // 0x2
field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
@@ -536,9 +540,14 @@ package android.app {
method public long getAccessDuration(int, int, int);
method public long getBackgroundAccessCount(int);
method public long getBackgroundAccessDuration(int);
+ method @NonNull public java.util.List<android.app.AppOpsManager.AttributedOpEntry> getBackgroundDiscreteAccesses(int);
method public long getBackgroundRejectCount(int);
+ method @NonNull public android.app.AppOpsManager.AttributedOpEntry getDiscreteAccessAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getDiscreteAccessCount();
+ method @NonNull public java.util.List<android.app.AppOpsManager.AttributedOpEntry> getDiscreteAccesses(int, int, int);
method public long getForegroundAccessCount(int);
method public long getForegroundAccessDuration(int);
+ method @NonNull public java.util.List<android.app.AppOpsManager.AttributedOpEntry> getForegroundDiscreteAccesses(int);
method public long getForegroundRejectCount(int);
method @NonNull public String getOpName();
method public long getRejectCount(int, int, int);
@@ -565,6 +574,7 @@ package android.app {
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
+ method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setHistoryFlags(int);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int);
@@ -2779,13 +2789,12 @@ package android.content.pm.verify.domain {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
}
- public interface DomainVerificationManager {
+ public final class DomainVerificationManager {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames();
method public static boolean isStateModifiable(int);
method public static boolean isStateVerified(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -2802,18 +2811,8 @@ package android.content.pm.verify.domain {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationRequest> CREATOR;
}
- public final class DomainVerificationUserSelection implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+ public final class DomainVerificationUserState implements android.os.Parcelable {
method @NonNull public java.util.UUID getIdentifier();
- method @NonNull public String getPackageName();
- method @NonNull public android.os.UserHandle getUser();
- method @NonNull public boolean isLinkHandlingAllowed();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR;
- field public static final int DOMAIN_STATE_NONE = 0; // 0x0
- field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
- field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
}
}
@@ -8835,6 +8834,8 @@ package android.provider {
field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SCHEDULER = "scheduler";
+ field public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
+ field public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
@@ -13049,7 +13050,7 @@ package android.telephony.ims {
method @NonNull public String getServiceId();
method @NonNull public String getServiceVersion();
method @NonNull public String getStatus();
- method @Nullable public String getTimestamp();
+ method @Nullable public java.time.Instant getTime();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
@@ -13076,7 +13077,7 @@ package android.telephony.ims {
method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
- method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTime(@NonNull java.time.Instant);
}
public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
@@ -13132,7 +13133,7 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
@@ -13608,7 +13609,7 @@ package android.telephony.ims.stub {
ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
- method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
+ method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1e5a6f12f96b..e4757e6204b7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -224,6 +224,9 @@ package android.app {
field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
+ field public static final int HISTORY_FLAGS_ALL = 3; // 0x3
+ field public static final int HISTORY_FLAG_AGGREGATE = 1; // 0x1
+ field public static final int HISTORY_FLAG_DISCRETE = 2; // 0x2
field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
@@ -238,6 +241,7 @@ package android.app {
public static final class AppOpsManager.HistoricalOps implements android.os.Parcelable {
ctor public AppOpsManager.HistoricalOps(long, long);
+ method public void addDiscreteAccess(int, int, @NonNull String, @Nullable String, int, int, long, long);
method public void increaseAccessCount(int, int, @NonNull String, @Nullable String, int, int, long);
method public void increaseAccessDuration(int, int, @NonNull String, @Nullable String, int, int, long);
method public void increaseRejectCount(int, int, @NonNull String, @Nullable String, int, int, long);
@@ -1485,6 +1489,7 @@ package android.os {
public abstract class CombinedVibrationEffect implements android.os.Parcelable {
method public abstract long getDuration();
+ method @NonNull public static android.os.CombinedVibrationEffect.SequentialCombination startSequential();
}
public static final class CombinedVibrationEffect.Mono extends android.os.CombinedVibrationEffect {
@@ -1502,6 +1507,14 @@ package android.os {
field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Sequential> CREATOR;
}
+ public static final class CombinedVibrationEffect.SequentialCombination {
+ method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect);
+ method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect, int);
+ method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect);
+ method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect, int);
+ method @NonNull public android.os.CombinedVibrationEffect combine();
+ }
+
public static final class CombinedVibrationEffect.Stereo extends android.os.CombinedVibrationEffect {
method public long getDuration();
method @NonNull public android.util.SparseArray<android.os.VibrationEffect> getEffects();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 730fce9449d0..e7751b861037 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -176,6 +176,8 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
+import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
import android.webkit.WebView;
import android.window.SplashScreen;
@@ -512,6 +514,8 @@ public final class ActivityThread extends ClientTransactionHandler {
boolean mHasImeComponent = false;
+ private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null;
+
/** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
public static final class ActivityClientRecord {
@UnsupportedAppUsage
@@ -1939,6 +1943,7 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int PURGE_RESOURCES = 161;
public static final int ATTACH_STARTUP_AGENTS = 162;
public static final int UPDATE_UI_TRANSLATION_STATE = 163;
+ public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164;
public static final int INSTRUMENT_WITHOUT_RESTART = 170;
public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -1988,6 +1993,8 @@ public final class ActivityThread extends ClientTransactionHandler {
case PURGE_RESOURCES: return "PURGE_RESOURCES";
case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE";
+ case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
+ return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK";
case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
@@ -2180,6 +2187,9 @@ public final class ActivityThread extends ClientTransactionHandler {
(TranslationSpec) args.arg3, (TranslationSpec) args.arg4,
(List<AutofillId>) args.arg5);
break;
+ case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
+ handleSetContentCaptureOptionsCallback((String) msg.obj);
+ break;
case INSTRUMENT_WITHOUT_RESTART:
handleInstrumentWithoutRestart((AppBindData) msg.obj);
break;
@@ -6795,6 +6805,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// Propagate Content Capture options
app.setContentCaptureOptions(data.contentCaptureOptions);
+ sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
mInitialApplication = app;
@@ -6856,6 +6867,36 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ private void handleSetContentCaptureOptionsCallback(String packageName) {
+ if (mContentCaptureOptionsCallback != null) {
+ return;
+ }
+
+ IBinder b = ServiceManager.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ if (b == null) {
+ return;
+ }
+
+ IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
+ mContentCaptureOptionsCallback = new IContentCaptureOptionsCallback.Stub() {
+ @Override
+ public void setContentCaptureOptions(ContentCaptureOptions options)
+ throws RemoteException {
+ if (mInitialApplication != null) {
+ mInitialApplication.setContentCaptureOptions(options);
+ }
+ }
+ };
+ try {
+ service.registerContentCaptureOptionsCallback(packageName,
+ mContentCaptureOptionsCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "registerContentCaptureOptionsCallback() failed: "
+ + packageName, e);
+ mContentCaptureOptionsCallback = null;
+ }
+ }
+
private void handleInstrumentWithoutRestart(AppBindData data) {
try {
data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 160844aacc46..dd1bc7c61547 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static java.lang.Long.max;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -3385,6 +3387,13 @@ public class AppOpsManager {
@DataClass.ParcelWith(LongSparseArrayParceling.class)
private final @Nullable LongSparseArray<NoteOpEvent> mRejectEvents;
+ private AttributedOpEntry(@NonNull AttributedOpEntry other) {
+ mOp = other.mOp;
+ mRunning = other.mRunning;
+ mAccessEvents = other.mAccessEvents == null ? null : other.mAccessEvents.clone();
+ mRejectEvents = other.mRejectEvents == null ? null : other.mRejectEvents.clone();
+ }
+
/**
* Returns all keys for which we have events.
*
@@ -3749,6 +3758,15 @@ public class AppOpsManager {
return lastEvent.getProxy();
}
+ @NonNull
+ String getOpName() {
+ return AppOpsManager.opToPublicName(mOp);
+ }
+
+ int getOp() {
+ return mOp;
+ }
+
private static class LongSparseArrayParceling implements
Parcelling<LongSparseArray<NoteOpEvent>> {
@Override
@@ -4571,6 +4589,50 @@ public class AppOpsManager {
}
/**
+ * Flag for querying app op history: get only aggregate information and no
+ * discrete accesses.
+ *
+ * @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public static final int HISTORY_FLAG_AGGREGATE = 1 << 0;
+
+ /**
+ * Flag for querying app op history: get only discrete information and no
+ * aggregate accesses.
+ *
+ * @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
+
+ /**
+ * Flag for querying app op history: get all types of historical accesses.
+ *
+ * @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public static final int HISTORY_FLAGS_ALL = HISTORY_FLAG_AGGREGATE
+ | HISTORY_FLAG_DISCRETE;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
+ HISTORY_FLAG_AGGREGATE,
+ HISTORY_FLAG_DISCRETE
+ })
+ public @interface OpHistoryFlags {}
+
+ /**
* Specifies what parameters to filter historical appop requests for
*
* @hide
@@ -4625,6 +4687,7 @@ public class AppOpsManager {
private final @Nullable String mPackageName;
private final @Nullable String mAttributionTag;
private final @Nullable List<String> mOpNames;
+ private final @OpHistoryFlags int mHistoryFlags;
private final @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
@@ -4632,12 +4695,13 @@ public class AppOpsManager {
private HistoricalOpsRequest(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable List<String> opNames,
- @HistoricalOpsRequestFilter int filter, long beginTimeMillis,
- long endTimeMillis, @OpFlags int flags) {
+ @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
+ long beginTimeMillis, long endTimeMillis, @OpFlags int flags) {
mUid = uid;
mPackageName = packageName;
mAttributionTag = attributionTag;
mOpNames = opNames;
+ mHistoryFlags = historyFlags;
mFilter = filter;
mBeginTimeMillis = beginTimeMillis;
mEndTimeMillis = endTimeMillis;
@@ -4655,6 +4719,7 @@ public class AppOpsManager {
private @Nullable String mPackageName;
private @Nullable String mAttributionTag;
private @Nullable List<String> mOpNames;
+ private @OpHistoryFlags int mHistoryFlags;
private @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
private final long mEndTimeMillis;
@@ -4676,6 +4741,7 @@ public class AppOpsManager {
"beginTimeMillis must be non negative and lesser than endTimeMillis");
mBeginTimeMillis = beginTimeMillis;
mEndTimeMillis = endTimeMillis;
+ mHistoryFlags = HISTORY_FLAG_AGGREGATE;
}
/**
@@ -4772,11 +4838,25 @@ public class AppOpsManager {
}
/**
+ * Specifies what type of historical information to query.
+ *
+ * @param flags Flags for the historical types to fetch which are any
+ * combination of {@link #HISTORY_FLAG_AGGREGATE}, {@link #HISTORY_FLAG_DISCRETE},
+ * {@link #HISTORY_FLAGS_ALL}. The default is {@link #HISTORY_FLAG_AGGREGATE}.
+ * @return This builder.
+ */
+ public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
+ Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL);
+ mHistoryFlags = flags;
+ return this;
+ }
+
+ /**
* @return a new {@link HistoricalOpsRequest}.
*/
public @NonNull HistoricalOpsRequest build() {
return new HistoricalOpsRequest(mUid, mPackageName, mAttributionTag, mOpNames,
- mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
+ mHistoryFlags, mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
}
}
}
@@ -4943,7 +5023,8 @@ public class AppOpsManager {
* @hide
*/
public void filter(int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
+ @Nullable String[] opNames, @OpHistoryFlags int historyFilter,
+ @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis) {
final long durationMillis = getDurationMillis();
mBeginTimeMillis = Math.max(mBeginTimeMillis, beginTimeMillis);
@@ -4956,7 +5037,8 @@ public class AppOpsManager {
if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
mHistoricalUidOps.removeAt(i);
} else {
- uidOp.filter(packageName, attributionTag, opNames, filter, scaleFactor);
+ uidOp.filter(packageName, attributionTag, opNames, filter, historyFilter,
+ scaleFactor, mBeginTimeMillis, mEndTimeMillis);
if (uidOp.getPackageCount() == 0) {
mHistoricalUidOps.removeAt(i);
}
@@ -5013,6 +5095,16 @@ public class AppOpsManager {
/** @hide */
@TestApi
+ public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
+ long discreteAccessTime, long discreteAccessDuration) {
+ getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration);
+ };
+
+
+ /** @hide */
+ @TestApi
public void offsetBeginAndEndTime(long offsetMillis) {
mBeginTimeMillis += offsetMillis;
mEndTimeMillis += offsetMillis;
@@ -5288,7 +5380,8 @@ public class AppOpsManager {
private void filter(@Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
- double fractionToRemove) {
+ @OpHistoryFlags int historyFilter, double fractionToRemove, long beginTimeMillis,
+ long endTimeMillis) {
final int packageCount = getPackageCount();
for (int i = packageCount - 1; i >= 0; i--) {
final HistoricalPackageOps packageOps = getPackageOpsAt(i);
@@ -5296,7 +5389,8 @@ public class AppOpsManager {
packageOps.getPackageName())) {
mHistoricalPackageOps.removeAt(i);
} else {
- packageOps.filter(attributionTag, opNames, filter, fractionToRemove);
+ packageOps.filter(attributionTag, opNames, filter, historyFilter,
+ fractionToRemove, beginTimeMillis, endTimeMillis);
if (packageOps.getAttributedOpsCount() == 0) {
mHistoricalPackageOps.removeAt(i);
}
@@ -5336,6 +5430,13 @@ public class AppOpsManager {
opCode, attributionTag, uidState, flags, increment);
}
+ private void addDiscreteAccess(int opCode, @NonNull String packageName,
+ @Nullable String attributionTag, @UidState int uidState,
+ @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) {
+ getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
+ uidState, flag, discreteAccessTime, discreteAccessDuration);
+ };
+
/**
* @return The UID for which the data is related.
*/
@@ -5540,7 +5641,8 @@ public class AppOpsManager {
}
private void filter(@Nullable String attributionTag, @Nullable String[] opNames,
- @HistoricalOpsRequestFilter int filter, double fractionToRemove) {
+ @HistoricalOpsRequestFilter int filter, @OpHistoryFlags int historyFilter,
+ double fractionToRemove, long beginTimeMillis, long endTimeMillis) {
final int attributionCount = getAttributedOpsCount();
for (int i = attributionCount - 1; i >= 0; i--) {
final AttributedHistoricalOps attributionOps = getAttributedOpsAt(i);
@@ -5548,7 +5650,8 @@ public class AppOpsManager {
attributionOps.getTag())) {
mAttributedHistoricalOps.removeAt(i);
} else {
- attributionOps.filter(opNames, filter, fractionToRemove);
+ attributionOps.filter(opNames, filter, historyFilter, fractionToRemove,
+ beginTimeMillis, endTimeMillis);
if (attributionOps.getOpCount() == 0) {
mAttributedHistoricalOps.removeAt(i);
}
@@ -5593,6 +5696,13 @@ public class AppOpsManager {
opCode, uidState, flags, increment);
}
+ private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
+ @UidState int uidState, @OpFlags int flag, long discreteAccessTime,
+ long discreteAccessDuration) {
+ getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
+ flag, discreteAccessTime, discreteAccessDuration);
+ }
+
/**
* Gets the package name which the data represents.
*
@@ -5870,7 +5980,8 @@ public class AppOpsManager {
}
private void filter(@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
- double scaleFactor) {
+ @OpHistoryFlags int historyFilter, double scaleFactor, long beginTimeMillis,
+ long endTimeMillis) {
final int opCount = getOpCount();
for (int i = opCount - 1; i >= 0; i--) {
final HistoricalOp op = mHistoricalOps.valueAt(i);
@@ -5878,7 +5989,7 @@ public class AppOpsManager {
op.getOpName())) {
mHistoricalOps.removeAt(i);
} else {
- op.filter(scaleFactor);
+ op.filter(historyFilter, scaleFactor, beginTimeMillis, endTimeMillis);
}
}
}
@@ -5909,6 +6020,12 @@ public class AppOpsManager {
getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, flags, increment);
}
+ private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
+ long discreteAccessTime, long discreteAccessDuration) {
+ getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
+ discreteAccessDuration);
+ }
+
/**
* Gets number historical app ops.
*
@@ -5970,8 +6087,6 @@ public class AppOpsManager {
return op;
}
-
-
// Code below generated by codegen v1.0.14.
//
// DO NOT MODIFY!
@@ -6121,6 +6236,9 @@ public class AppOpsManager {
private @Nullable LongSparseLongArray mRejectCount;
private @Nullable LongSparseLongArray mAccessDuration;
+ /** Discrete Ops for this Op */
+ private @Nullable List<AttributedOpEntry> mDiscreteAccesses;
+
/** @hide */
public HistoricalOp(int op) {
mOp = op;
@@ -6137,6 +6255,12 @@ public class AppOpsManager {
if (other.mAccessDuration != null) {
mAccessDuration = other.mAccessDuration.clone();
}
+ final int historicalOpCount = other.getDiscreteAccessCount();
+ for (int i = 0; i < historicalOpCount; i++) {
+ final AttributedOpEntry origOp = other.getDiscreteAccessAt(i);
+ final AttributedOpEntry cloneOp = new AttributedOpEntry(origOp);
+ getOrCreateDiscreteAccesses().add(cloneOp);
+ }
}
private HistoricalOp(@NonNull Parcel parcel) {
@@ -6144,22 +6268,45 @@ public class AppOpsManager {
mAccessCount = readLongSparseLongArrayFromParcel(parcel);
mRejectCount = readLongSparseLongArrayFromParcel(parcel);
mAccessDuration = readLongSparseLongArrayFromParcel(parcel);
+ mDiscreteAccesses = readDiscreteAccessArrayFromParcel(parcel);
}
- private void filter(double scaleFactor) {
- scale(mAccessCount, scaleFactor);
- scale(mRejectCount, scaleFactor);
- scale(mAccessDuration, scaleFactor);
+ private void filter(@OpHistoryFlags int historyFlag, double scaleFactor,
+ long beginTimeMillis, long endTimeMillis) {
+ if ((historyFlag & HISTORY_FLAG_AGGREGATE) == 0) {
+ mAccessCount = null;
+ mRejectCount = null;
+ mAccessDuration = null;
+ } else {
+ scale(mAccessCount, scaleFactor);
+ scale(mRejectCount, scaleFactor);
+ scale(mAccessDuration, scaleFactor);
+ }
+ if ((historyFlag & HISTORY_FLAG_DISCRETE) == 0) {
+ mDiscreteAccesses = null;
+ return;
+ }
+ final int discreteOpCount = getDiscreteAccessCount();
+ for (int i = discreteOpCount - 1; i >= 0; i--) {
+ final AttributedOpEntry op = mDiscreteAccesses.get(i);
+ long opBeginTime = op.getLastAccessTime(OP_FLAGS_ALL);
+ long opEndTime = opBeginTime + op.getLastDuration(OP_FLAGS_ALL);
+ opEndTime = max(opBeginTime, opEndTime);
+ if (opEndTime < beginTimeMillis || opBeginTime > endTimeMillis) {
+ mDiscreteAccesses.remove(i);
+ }
+ }
}
private boolean isEmpty() {
return !hasData(mAccessCount)
&& !hasData(mRejectCount)
- && !hasData(mAccessDuration);
+ && !hasData(mAccessDuration)
+ && (mDiscreteAccesses == null);
}
private boolean hasData(@NonNull LongSparseLongArray array) {
- return (array != null && array.size() > 0);
+ return array != null && array.size() > 0;
}
private @Nullable HistoricalOp splice(double fractionToRemove) {
@@ -6191,6 +6338,32 @@ public class AppOpsManager {
merge(this::getOrCreateAccessCount, other.mAccessCount);
merge(this::getOrCreateRejectCount, other.mRejectCount);
merge(this::getOrCreateAccessDuration, other.mAccessDuration);
+
+ if (other.mDiscreteAccesses == null) {
+ return;
+ }
+ if (mDiscreteAccesses == null) {
+ mDiscreteAccesses = new ArrayList(other.mDiscreteAccesses);
+ return;
+ }
+ List<AttributedOpEntry> historicalDiscreteAccesses = new ArrayList<>();
+ final int otherHistoricalOpCount = other.getDiscreteAccessCount();
+ final int historicalOpCount = getDiscreteAccessCount();
+ int i = 0;
+ int j = 0;
+ while (i < otherHistoricalOpCount || j < historicalOpCount) {
+ if (i == otherHistoricalOpCount) {
+ historicalDiscreteAccesses.add(mDiscreteAccesses.get(j++));
+ } else if (j == historicalOpCount) {
+ historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
+ } else if (mDiscreteAccesses.get(j).getLastAccessTime(OP_FLAGS_ALL)
+ < other.mDiscreteAccesses.get(i).getLastAccessTime(OP_FLAGS_ALL)) {
+ historicalDiscreteAccesses.add(mDiscreteAccesses.get(j++));
+ } else {
+ historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
+ }
+ }
+ mDiscreteAccesses = historicalDiscreteAccesses;
}
private void increaseAccessCount(@UidState int uidState, @OpFlags int flags,
@@ -6218,6 +6391,23 @@ public class AppOpsManager {
}
}
+ private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
+ long discreteAccessTime, long discreteAccessDuration) {
+ List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
+ LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
+ long key = makeKey(uidState, flag);
+ NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
+ accessEvents.append(key, note);
+ AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
+ for (int i = discreteAccesses.size() - 1; i >= 0; i--) {
+ if (discreteAccesses.get(i).getLastAccessTime(OP_FLAGS_ALL) < discreteAccessTime) {
+ discreteAccesses.add(i + 1, access);
+ return;
+ }
+ }
+ discreteAccesses.add(0, access);
+ }
+
/**
* Gets the op name.
*
@@ -6233,6 +6423,33 @@ public class AppOpsManager {
}
/**
+ * Gets number of discrete historical app ops.
+ *
+ * @return The number historical app ops.
+ * @see #getOpAt(int)
+ */
+ public @IntRange(from = 0) int getDiscreteAccessCount() {
+ if (mDiscreteAccesses == null) {
+ return 0;
+ }
+ return mDiscreteAccesses.size();
+ }
+
+ /**
+ * Gets the historical op at a given index.
+ *
+ * @param index The index to lookup.
+ * @return The op at the given index.
+ * @see #getOpCount()
+ */
+ public @NonNull AttributedOpEntry getDiscreteAccessAt(@IntRange(from = 0) int index) {
+ if (mDiscreteAccesses == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return mDiscreteAccesses.get(index);
+ }
+
+ /**
* Gets the number times the op was accessed (performed) in the foreground.
*
* @param flags The flags which are any combination of
@@ -6251,6 +6468,25 @@ public class AppOpsManager {
}
/**
+ * Gets the discrete events the op was accessed (performed) in the foreground.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return The list of discrete ops accessed in the foreground.
+ *
+ * @see #getBackgroundDiscreteAccesses(int)
+ * @see #getDiscreteAccesses(int, int, int)
+ */
+ @NonNull
+ public List<AttributedOpEntry> getForegroundDiscreteAccesses(@OpFlags int flags) {
+ return listForFlagsInStates(mDiscreteAccesses, MAX_PRIORITY_UID_STATE,
+ resolveFirstUnrestrictedUidState(mOp), flags);
+ }
+
+ /**
* Gets the number times the op was accessed (performed) in the background.
*
* @param flags The flags which are any combination of
@@ -6269,6 +6505,25 @@ public class AppOpsManager {
}
/**
+ * Gets the discrete events the op was accessed (performed) in the background.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return The list of discrete ops accessed in the background.
+ *
+ * @see #getForegroundDiscreteAccesses(int)
+ * @see #getDiscreteAccesses(int, int, int)
+ */
+ @NonNull
+ public List<AttributedOpEntry> getBackgroundDiscreteAccesses(@OpFlags int flags) {
+ return listForFlagsInStates(mDiscreteAccesses, resolveLastRestrictedUidState(mOp),
+ MIN_PRIORITY_UID_STATE, flags);
+ }
+
+ /**
* Gets the number times the op was accessed (performed) for a
* range of uid states.
*
@@ -6294,6 +6549,26 @@ public class AppOpsManager {
}
/**
+ * Gets the discrete events the op was accessed (performed) for a
+ * range of uid states.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return The discrete the op was accessed in the background.
+ *
+ * @see #getBackgroundDiscreteAccesses(int)
+ * @see #getForegroundDiscreteAccesses(int)
+ */
+ @NonNull
+ public List<AttributedOpEntry> getDiscreteAccesses(@UidState int fromUidState,
+ @UidState int toUidState, @OpFlags int flags) {
+ return listForFlagsInStates(mDiscreteAccesses, fromUidState, toUidState, flags);
+ }
+
+ /**
* Gets the number times the op was rejected in the foreground.
*
* @param flags The flags which are any combination of
@@ -6427,6 +6702,7 @@ public class AppOpsManager {
writeLongSparseLongArrayToParcel(mAccessCount, parcel);
writeLongSparseLongArrayToParcel(mRejectCount, parcel);
writeLongSparseLongArrayToParcel(mAccessDuration, parcel);
+ writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel);
}
@Override
@@ -6447,7 +6723,11 @@ public class AppOpsManager {
if (!equalsLongSparseLongArray(mRejectCount, other.mRejectCount)) {
return false;
}
- return equalsLongSparseLongArray(mAccessDuration, other.mAccessDuration);
+ if (!equalsLongSparseLongArray(mAccessDuration, other.mAccessDuration)) {
+ return false;
+ }
+ return mDiscreteAccesses == null ? (other.mDiscreteAccesses == null ? true
+ : false) : mDiscreteAccesses.equals(other.mDiscreteAccesses);
}
@Override
@@ -6456,6 +6736,7 @@ public class AppOpsManager {
result = 31 * result + Objects.hashCode(mAccessCount);
result = 31 * result + Objects.hashCode(mRejectCount);
result = 31 * result + Objects.hashCode(mAccessDuration);
+ result = 31 * result + Objects.hashCode(mDiscreteAccesses);
return result;
}
@@ -6484,6 +6765,13 @@ public class AppOpsManager {
return mAccessDuration;
}
+ private @NonNull List<AttributedOpEntry> getOrCreateDiscreteAccesses() {
+ if (mDiscreteAccesses == null) {
+ mDiscreteAccesses = new ArrayList<>();
+ }
+ return mDiscreteAccesses;
+ }
+
/**
* Multiplies the entries in the array with the passed in scale factor and
* rounds the result at up 0.5 boundary.
@@ -6574,6 +6862,32 @@ public class AppOpsManager {
}
/**
+ * Returns list of events filtered by UidState and UID flags.
+ *
+ * @param accesses The events list.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
+ * @param flags The UID flags.
+ * @return filtered list of events.
+ */
+ private static List<AttributedOpEntry> listForFlagsInStates(List<AttributedOpEntry> accesses,
+ @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
+ List<AttributedOpEntry> result = new ArrayList<>();
+ if (accesses == null) {
+ return result;
+ }
+ int nAccesses = accesses.size();
+ for (int i = 0; i < nAccesses; i++) {
+ AttributedOpEntry entry = accesses.get(i);
+ if (entry.getLastAccessTime(beginUidState, endUidState, flags) == -1) {
+ continue;
+ }
+ result.add(entry);
+ }
+ return result;
+ }
+
+ /**
* Callback for notification of changes to operation state.
*/
public interface OnOpChangedListener {
@@ -6796,8 +7110,9 @@ public class AppOpsManager {
Objects.requireNonNull(callback, "callback cannot be null");
try {
mService.getHistoricalOps(request.mUid, request.mPackageName, request.mAttributionTag,
- request.mOpNames, request.mFilter, request.mBeginTimeMillis,
- request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
+ request.mOpNames, request.mHistoryFlags, request.mFilter,
+ request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
+ new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
@@ -6835,9 +7150,9 @@ public class AppOpsManager {
Objects.requireNonNull(callback, "callback cannot be null");
try {
mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
- request.mAttributionTag, request.mOpNames, request.mFilter,
- request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
- new RemoteCallback((result) -> {
+ request.mAttributionTag, request.mOpNames, request.mHistoryFlags,
+ request.mFilter, request.mBeginTimeMillis, request.mEndTimeMillis,
+ request.mFlags, new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
@@ -9072,6 +9387,32 @@ public class AppOpsManager {
return array;
}
+ private static void writeDiscreteAccessArrayToParcel(
+ @Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel) {
+ if (array != null) {
+ final int size = array.size();
+ parcel.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ array.get(i).writeToParcel(parcel, 0);
+ }
+ } else {
+ parcel.writeInt(-1);
+ }
+ }
+
+ private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel(
+ @NonNull Parcel parcel) {
+ final int size = parcel.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final List<AttributedOpEntry> array = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ array.add(new AttributedOpEntry(parcel));
+ }
+ return array;
+ }
+
/**
* Collects the keys from an array to the result creating the result if needed.
*
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8167622ff13c..bc24e9767944 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -24,6 +24,7 @@ import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
import static java.util.Objects.requireNonNull;
+import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.ColorRes;
import android.annotation.DimenRes;
@@ -98,6 +99,7 @@ import android.widget.RemoteViews;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
@@ -3649,11 +3651,6 @@ public class Notification implements Parcelable
private int mCachedContrastColorIsFor = COLOR_INVALID;
/**
- * A neutral color color that can be used for icons.
- */
- private int mNeutralColor = COLOR_INVALID;
-
- /**
* Caches an instance of StandardTemplateParams. Note that this may have been used before,
* so make sure to call {@link StandardTemplateParams#reset()} before using it.
*/
@@ -3666,6 +3663,7 @@ public class Notification implements Parcelable
private boolean mRebuildStyledRemoteViews;
private boolean mTintActionButtons;
+ private boolean mTintWithThemeAccent;
private boolean mInNightMode;
/**
@@ -3701,6 +3699,7 @@ public class Notification implements Parcelable
mContext = context;
Resources res = mContext.getResources();
mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);
+ mTintWithThemeAccent = res.getBoolean(R.bool.config_tintNotificationsWithTheme);
if (res.getBoolean(R.bool.config_enableNightMode)) {
Configuration currentConfig = res.getConfiguration();
@@ -4891,12 +4890,10 @@ public class Notification implements Parcelable
}
private void bindPhishingAlertIcon(RemoteViews contentView, StandardTemplateParams p) {
- // TODO(b/180334837): Get buy-in on this color, or make sure to give this the
- // accent color, while still accommodating the colorized state.
contentView.setDrawableTint(
R.id.phishing_alert,
false /* targetBackground */,
- getPrimaryTextColor(p),
+ getErrorColor(p),
PorterDuff.Mode.SRC_ATOP);
}
@@ -4943,7 +4940,7 @@ public class Notification implements Parcelable
contentView.setDrawableTint(
R.id.alerted_icon,
false /* targetBackground */,
- getNeutralColor(p),
+ getHeaderIconColor(p),
PorterDuff.Mode.SRC_ATOP);
}
@@ -5057,10 +5054,9 @@ public class Notification implements Parcelable
return text;
}
- private void setTextViewColorPrimary(RemoteViews contentView, int id,
+ private void setTextViewColorPrimary(RemoteViews contentView, @IdRes int id,
StandardTemplateParams p) {
- ensureColors(p);
- contentView.setTextColor(id, mPrimaryTextColor);
+ contentView.setTextColor(id, getPrimaryTextColor(p));
}
private boolean hasForegroundColor() {
@@ -5068,53 +5064,34 @@ public class Notification implements Parcelable
}
/**
- * Return the primary text color using the existing template params
- * @hide
- */
- @VisibleForTesting
- public int getPrimaryTextColor() {
- return getPrimaryTextColor(mParams);
- }
-
- /**
* @param p the template params to inflate this with
* @return the primary text color
* @hide
*/
@VisibleForTesting
- public int getPrimaryTextColor(StandardTemplateParams p) {
+ public @ColorInt int getPrimaryTextColor(StandardTemplateParams p) {
ensureColors(p);
return mPrimaryTextColor;
}
/**
- * Return the secondary text color using the existing template params
- * @hide
- */
- @VisibleForTesting
- public int getSecondaryTextColor() {
- return getSecondaryTextColor(mParams);
- }
-
- /**
* @param p the template params to inflate this with
* @return the secondary text color
* @hide
*/
@VisibleForTesting
- public int getSecondaryTextColor(StandardTemplateParams p) {
+ public @ColorInt int getSecondaryTextColor(StandardTemplateParams p) {
ensureColors(p);
return mSecondaryTextColor;
}
- private void setTextViewColorSecondary(RemoteViews contentView, int id,
+ private void setTextViewColorSecondary(RemoteViews contentView, @IdRes int id,
StandardTemplateParams p) {
- ensureColors(p);
- contentView.setTextColor(id, mSecondaryTextColor);
+ contentView.setTextColor(id, getSecondaryTextColor(p));
}
private void ensureColors(StandardTemplateParams p) {
- int backgroundColor = getBackgroundColor(p);
+ int backgroundColor = getUnresolvedBackgroundColor(p);
if (mPrimaryTextColor == COLOR_INVALID
|| mSecondaryTextColor == COLOR_INVALID
|| mTextColorsAreForBackground != backgroundColor) {
@@ -5217,7 +5194,7 @@ public class Notification implements Parcelable
R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
if (getRawColor(p) != COLOR_DEFAULT) {
- int color = isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p);
+ int color = getAccentColor(p);
ColorStateList colorStateList = ColorStateList.valueOf(color);
contentView.setProgressTintList(R.id.progress, colorStateList);
contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
@@ -5326,11 +5303,18 @@ public class Notification implements Parcelable
}
private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
- int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
- contentView.setDrawableTint(R.id.expand_button, false, color,
- PorterDuff.Mode.SRC_ATOP);
- contentView.setInt(R.id.expand_button, "setOriginalNotificationColor",
- color);
+ // set default colors
+ int textColor = getPrimaryTextColor(p);
+ int pillColor = getProtectionColor(p);
+ contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor);
+ contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
+ // Use different highlighted colors except when low-priority mode prevents that
+ if (!p.forceDefaultColor) {
+ textColor = getBackgroundColor(p);
+ pillColor = getAccentColor(p);
+ }
+ contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
+ contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
}
private void bindHeaderChronometerAndTime(RemoteViews contentView,
@@ -5461,11 +5445,7 @@ public class Notification implements Parcelable
}
contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE);
contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
- if (isColorized(p)) {
- setTextViewColorPrimary(contentView, R.id.app_name_text, p);
- } else {
- contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
- }
+ contentView.setTextColor(R.id.app_name_text, getSecondaryTextColor(p));
return true;
}
@@ -5555,6 +5535,10 @@ public class Notification implements Parcelable
resetStandardTemplateWithActions(big);
bindSnoozeAction(big, p);
+ // color the snooze and bubble actions with the theme color
+ ColorStateList actionColor = ColorStateList.valueOf(getStandardActionColor(p));
+ big.setColorStateList(R.id.snooze_button, "setImageTintList", actionColor);
+ big.setColorStateList(R.id.bubble_button, "setImageTintList", actionColor);
boolean validRemoteInput = false;
@@ -5604,8 +5588,7 @@ public class Notification implements Parcelable
showSpinner ? View.VISIBLE : View.GONE);
big.setProgressIndeterminateTintList(
R.id.notification_material_reply_progress,
- ColorStateList.valueOf(
- isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
+ ColorStateList.valueOf(getAccentColor(p)));
if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText())
&& p.maxRemoteInputHistory > 1) {
@@ -6021,14 +6004,14 @@ public class Notification implements Parcelable
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = new ColorStateList[1];
- int background = resolveBackgroundColor(p);
+ int background = getBackgroundColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- int textColor = getPrimaryTextColor(p);
+ final int textColor;
boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
@@ -6036,9 +6019,11 @@ public class Notification implements Parcelable
background = outResultColor[0].getDefaultColor();
textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
background, mInNightMode);
- } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
- && mTintActionButtons && !mInNightMode) {
- textColor = resolveContrastColor(p);
+ } else if (mTintActionButtons && !mInNightMode
+ && getRawColor(p) != COLOR_DEFAULT && !isColorized(p)) {
+ textColor = getAccentColor(p);
+ } else {
+ textColor = getPrimaryTextColor(p);
}
button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
@@ -6056,11 +6041,7 @@ public class Notification implements Parcelable
} else {
button.setTextViewText(R.id.action0, processTextSpans(
processLegacyText(action.title)));
- if (isColorized(p)) {
- setTextViewColorPrimary(button, R.id.action0, p);
- } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
- button.setTextColor(R.id.action0, resolveContrastColor(p));
- }
+ button.setTextColor(R.id.action0, getStandardActionColor(p));
}
// CallStyle notifications add action buttons which don't actually exist in mActions,
// so we have to omit the index in that case.
@@ -6170,9 +6151,9 @@ public class Notification implements Parcelable
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
StandardTemplateParams p) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
- int color = isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p);
+ int color = getSmallIconColor(p);
contentView.setInt(R.id.icon, "setBackgroundColor",
- resolveBackgroundColor(p));
+ getBackgroundColor(p));
contentView.setInt(R.id.icon, "setOriginalIconColor",
colorable ? color : COLOR_INVALID);
}
@@ -6187,7 +6168,7 @@ public class Notification implements Parcelable
if (largeIcon != null && isLegacy()
&& getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
// resolve color will fall back to the default when legacy
- int color = resolveContrastColor(p);
+ int color = getContrastColor(p);
contentView.setInt(R.id.icon, "setOriginalIconColor", color);
}
}
@@ -6198,14 +6179,94 @@ public class Notification implements Parcelable
}
}
- int resolveContrastColor(StandardTemplateParams p) {
+ /**
+ * Gets the standard action button color
+ */
+ private @ColorInt int getStandardActionColor(Notification.StandardTemplateParams p) {
+ return mTintActionButtons || isColorized(p) ? getAccentColor(p) : getNeutralColor(p);
+ }
+
+ /**
+ * Gets a neutral color that can be used for icons or similar that should not stand out.
+ */
+ private @ColorInt int getHeaderIconColor(StandardTemplateParams p) {
+ return isColorized(p) ? getSecondaryTextColor(p) : getNeutralColor(p);
+ }
+
+ /**
+ * Gets the foreground color of the small icon. If the notification is colorized, this
+ * is the primary text color, otherwise it's the contrast-adjusted app-provided color.
+ */
+ private @ColorInt int getSmallIconColor(StandardTemplateParams p) {
+ return isColorized(p) ? getPrimaryTextColor(p) : getContrastColor(p);
+ }
+
+ /**
+ * Gets the accent color for colored UI elements. If we're tinting with the theme
+ * accent, this is the theme accent color, otherwise this would be identical to
+ * {@link #getSmallIconColor(StandardTemplateParams)}.
+ */
+ private @ColorInt int getAccentColor(StandardTemplateParams p) {
+ if (isColorized(p)) {
+ return getPrimaryTextColor(p);
+ }
+ if (mTintWithThemeAccent) {
+ int color = obtainThemeColor(R.attr.colorAccent, COLOR_INVALID);
+ if (color != COLOR_INVALID) {
+ return color;
+ }
+ }
+ return getContrastColor(p);
+ }
+
+ /**
+ * Gets the "surface protection" color from the theme, or a variant of the normal background
+ * color when colorized, or when not using theme color tints.
+ */
+ private @ColorInt int getProtectionColor(StandardTemplateParams p) {
+ if (mTintWithThemeAccent && !isColorized(p)) {
+ int color = obtainThemeColor(R.attr.colorBackgroundFloating, COLOR_INVALID);
+ if (color != COLOR_INVALID) {
+ return color;
+ }
+ }
+ // TODO(b/181048615): What color should we use for the expander pill when colorized
+ return ColorUtils.blendARGB(getPrimaryTextColor(p), getBackgroundColor(p), 0.8f);
+ }
+
+ /**
+ * Gets the theme's error color, or the primary text color for colorized notifications.
+ */
+ private @ColorInt int getErrorColor(StandardTemplateParams p) {
+ if (!isColorized(p)) {
+ int color = obtainThemeColor(R.attr.colorError, COLOR_INVALID);
+ if (color != COLOR_INVALID) {
+ return color;
+ }
+ }
+ return getPrimaryTextColor(p);
+ }
+
+ /**
+ * Gets the theme's background color
+ */
+ private @ColorInt int getDefaultBackgroundColor() {
+ return obtainThemeColor(R.attr.colorBackground,
+ mInNightMode ? Color.BLACK : Color.WHITE);
+ }
+
+ /**
+ * Gets the contrast-adjusted version of the color provided by the app.
+ */
+ private @ColorInt int getContrastColor(StandardTemplateParams p) {
int rawColor = getRawColor(p);
if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
int color;
- int background = obtainBackgroundColor();
+ // TODO: Maybe use getBackgroundColor(p) instead -- but doing so could break the cache
+ int background = getDefaultBackgroundColor();
if (rawColor == COLOR_DEFAULT) {
ensureColors(p);
color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
@@ -6224,28 +6285,29 @@ public class Notification implements Parcelable
/**
* Return the raw color of this Notification, which doesn't necessarily satisfy contrast.
*
- * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color
+ * @see #getContrastColor(StandardTemplateParams) for the contrasted color
* @param p the template params to inflate this with
*/
- private int getRawColor(StandardTemplateParams p) {
+ private @ColorInt int getRawColor(StandardTemplateParams p) {
if (p.forceDefaultColor) {
return COLOR_DEFAULT;
}
return mN.color;
}
- int resolveNeutralColor() {
- if (mNeutralColor != COLOR_INVALID) {
- return mNeutralColor;
- }
- int background = obtainBackgroundColor();
- mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background,
+ /**
+ * Gets a neutral palette color; this is a contrast-satisfied version of the default color.
+ * @param p the template params to inflate this with
+ */
+ private @ColorInt int getNeutralColor(StandardTemplateParams p) {
+ int background = getBackgroundColor(p);
+ int neutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background,
mInNightMode);
- if (Color.alpha(mNeutralColor) < 255) {
+ if (Color.alpha(neutralColor) < 255) {
// alpha doesn't go well for color filters, so let's blend it manually
- mNeutralColor = ContrastColorUtil.compositeColors(mNeutralColor, background);
+ neutralColor = ContrastColorUtil.compositeColors(neutralColor, background);
}
- return mNeutralColor;
+ return neutralColor;
}
/**
@@ -6389,8 +6451,11 @@ public class Notification implements Parcelable
return mN;
}
- private @ColorInt int obtainBackgroundColor() {
- int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE;
+ /**
+ * Returns the color for the given Theme.DeviceDefault.DayNight attribute, or
+ * defValue if that could not be completed
+ */
+ private @ColorInt int obtainThemeColor(@AttrRes int attrRes, @ColorInt int defaultColor) {
Resources.Theme theme = mContext.getTheme();
if (theme == null) {
// Running unit tests with mocked context
@@ -6398,7 +6463,7 @@ public class Notification implements Parcelable
}
theme = new ContextThemeWrapper(mContext, R.style.Theme_DeviceDefault_DayNight)
.getTheme();
- TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground});
+ TypedArray ta = theme.obtainStyledAttributes(new int[]{attrRes});
if (ta == null) {
return defaultColor;
}
@@ -6517,42 +6582,30 @@ public class Notification implements Parcelable
return R.layout.notification_material_action_tombstone;
}
- private int getBackgroundColor(StandardTemplateParams p) {
- if (isColorized(p)) {
- return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
- } else {
- return COLOR_DEFAULT;
- }
- }
-
/**
- * Gets a neutral color that can be used for icons or similar that should not stand out.
- * @param p the template params to inflate this with
+ * Gets the background color, with {@link #COLOR_DEFAULT} being a valid return value,
+ * which must be resolved by the caller before being used.
*/
- private int getNeutralColor(StandardTemplateParams p) {
+ private @ColorInt int getUnresolvedBackgroundColor(StandardTemplateParams p) {
if (isColorized(p)) {
- return getSecondaryTextColor(p);
+ return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
} else {
- return resolveNeutralColor();
+ return COLOR_DEFAULT;
}
}
/**
- * Same as getBackgroundColor but also resolved the default color to the background.
- * @param p the template params to inflate this with
+ * Same as {@link #getUnresolvedBackgroundColor(StandardTemplateParams)} except that it
+ * also resolves the default color to the background.
*/
- private int resolveBackgroundColor(StandardTemplateParams p) {
- int backgroundColor = getBackgroundColor(p);
+ private @ColorInt int getBackgroundColor(StandardTemplateParams p) {
+ int backgroundColor = getUnresolvedBackgroundColor(p);
if (backgroundColor == COLOR_DEFAULT) {
- backgroundColor = obtainBackgroundColor();
+ backgroundColor = getDefaultBackgroundColor();
}
return backgroundColor;
}
- private boolean shouldTintActionButtons() {
- return mTintActionButtons;
- }
-
private boolean textColorsNeedInversion() {
if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
return false;
@@ -6570,7 +6623,7 @@ public class Notification implements Parcelable
*
* @hide
*/
- public void setColorPalette(int backgroundColor, int foregroundColor) {
+ public void setColorPalette(@ColorInt int backgroundColor, @ColorInt int foregroundColor) {
mBackgroundColor = backgroundColor;
mForegroundColor = foregroundColor;
mTextColorsAreForBackground = COLOR_INVALID;
@@ -8200,16 +8253,14 @@ public class Notification implements Parcelable
TypedValue.COMPLEX_UNIT_DIP);
}
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
- mBuilder.isColorized(p)
- ? mBuilder.getPrimaryTextColor(p)
- : mBuilder.resolveContrastColor(p));
+ mBuilder.getSmallIconColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
mBuilder.getPrimaryTextColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
mBuilder.getSecondaryTextColor(p));
contentView.setInt(R.id.status_bar_latest_event_content,
"setNotificationBackgroundColor",
- mBuilder.resolveBackgroundColor(p));
+ mBuilder.getBackgroundColor(p));
contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
isCollapsed);
contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
@@ -8964,14 +9015,7 @@ public class Notification implements Parcelable
// If the action buttons should not be tinted, then just use the default
// notification color. Otherwise, just use the passed-in color.
- Resources resources = mBuilder.mContext.getResources();
- Configuration currentConfig = resources.getConfiguration();
- boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p)
- ? getActionColor(p)
- : ContrastColorUtil.resolveColor(mBuilder.mContext,
- Notification.COLOR_DEFAULT, inNightMode);
+ int tintColor = mBuilder.getStandardActionColor(p);
container.setDrawableTint(buttonId, false, tintColor,
PorterDuff.Mode.SRC_ATOP);
@@ -9027,11 +9071,6 @@ public class Notification implements Parcelable
return view;
}
- private int getActionColor(StandardTemplateParams p) {
- return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
- : mBuilder.resolveContrastColor(p);
- }
-
private RemoteViews makeMediaBigContentView() {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
// Dont add an expanded view if there is no more content to be revealed
@@ -9373,7 +9412,6 @@ public class Notification implements Parcelable
.hideLargeIcon(true)
.text(text)
.summaryText(mBuilder.processLegacyText(mVerificationText));
- // TODO(b/179178086): hide the snooze button
RemoteViews contentView = mBuilder.applyStandardTemplate(
mBuilder.getCallLayoutResource(), p, null /* result */);
@@ -9390,11 +9428,9 @@ public class Notification implements Parcelable
// Bind some custom CallLayout properties
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
- mBuilder.isColorized(p)
- ? mBuilder.getPrimaryTextColor(p)
- : mBuilder.resolveContrastColor(p));
+ mBuilder.getSmallIconColor(p));
contentView.setInt(R.id.status_bar_latest_event_content,
- "setNotificationBackgroundColor", mBuilder.resolveBackgroundColor(p));
+ "setNotificationBackgroundColor", mBuilder.getBackgroundColor(p));
contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
mBuilder.mN.mLargeIcon);
contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e16e40b6d572..43c14a99b221 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -71,7 +71,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManagerImpl;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.content.res.Resources;
import android.content.rollback.RollbackManagerFrameworkInitializer;
@@ -1422,7 +1421,6 @@ public final class SystemServiceRegistry {
}
});
- // TODO(b/159952358): Only register this service for the domain verification agent?
registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class,
new CachedServiceFetcher<DomainVerificationManager>() {
@Override
@@ -1432,7 +1430,7 @@ public final class SystemServiceRegistry {
Context.DOMAIN_VERIFICATION_SERVICE);
IDomainVerificationManager service =
IDomainVerificationManager.Stub.asInterface(binder);
- return new DomainVerificationManagerImpl(context, service);
+ return new DomainVerificationManager(context, service);
}
});
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b919bfc92e79..0635bd08e22b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2997,6 +2997,7 @@ public class DevicePolicyManager {
*/
// TODO(b/173541467): should it throw SecurityException if caller is not admin?
public boolean isSafeOperation(@OperationSafetyReason int reason) {
+ throwIfParentInstance("isSafeOperation");
if (mService == null) return false;
try {
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 22492ccd0373..94a4fde0131e 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -403,7 +403,7 @@ public abstract class BackupAgent extends ContextWrapper {
public void onFullBackup(FullBackupDataOutput data) throws IOException {
FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this,
mOperationType);
- if (!isDeviceToDeviceMigration() && !backupScheme.isFullBackupContentEnabled()) {
+ if (!backupScheme.isFullBackupEnabled(data.getTransportFlags())) {
return;
}
@@ -911,7 +911,7 @@ public abstract class BackupAgent extends ContextWrapper {
}
FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
- if (!bs.isFullBackupContentEnabled()) {
+ if (!bs.isFullRestoreEnabled()) {
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(FullBackup.TAG_XML_PARSER,
"onRestoreFile \"" + destination.getCanonicalPath()
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 829b6cd43934..9b543b571a44 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -99,6 +99,8 @@ public class FullBackup {
public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer";
public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
"fakeClientSideEncryption";
+ private static final String FLAG_DISABLE_IF_NO_ENCRYPTION_CAPABILITIES
+ = "disableIfNoEncryptionCapabilities";
/**
* When this change is enabled, include / exclude rules specified via
@@ -307,6 +309,10 @@ public class FullBackup {
// lazy initialized, only when needed
private StorageVolume[] mVolumes = null;
+ // Properties the transport must have (e.g. encryption) for the operation to go ahead.
+ @Nullable private Integer mRequiredTransportFlags;
+ @Nullable private Boolean mIsUsingNewScheme;
+
/**
* Parse out the semantic domains into the correct physical location.
*/
@@ -453,6 +459,35 @@ public class FullBackup {
}
}
+ boolean isFullBackupEnabled(int transportFlags) {
+ try {
+ if (isUsingNewScheme()) {
+ int requiredTransportFlags = getRequiredTransportFlags();
+ // All bits that are set in requiredTransportFlags must be set in
+ // transportFlags.
+ return (transportFlags & requiredTransportFlags) == requiredTransportFlags;
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "Failed to interpret the backup scheme: " + e);
+ return false;
+ }
+
+ return isFullBackupContentEnabled();
+ }
+
+ boolean isFullRestoreEnabled() {
+ try {
+ if (isUsingNewScheme()) {
+ return true;
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "Failed to interpret the backup scheme: " + e);
+ return false;
+ }
+
+ return isFullBackupContentEnabled();
+ }
+
boolean isFullBackupContentEnabled() {
if (mFullBackupContent < 0) {
// android:fullBackupContent="false", bail.
@@ -491,10 +526,30 @@ public class FullBackup {
return mExcludes;
}
+ private synchronized int getRequiredTransportFlags()
+ throws IOException, XmlPullParserException {
+ if (mRequiredTransportFlags == null) {
+ maybeParseBackupSchemeLocked();
+ }
+
+ return mRequiredTransportFlags;
+ }
+
+ private synchronized boolean isUsingNewScheme()
+ throws IOException, XmlPullParserException {
+ if (mIsUsingNewScheme == null) {
+ maybeParseBackupSchemeLocked();
+ }
+
+ return mIsUsingNewScheme;
+ }
+
private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException {
// This not being null is how we know that we've tried to parse the xml already.
mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>();
mExcludes = new ArraySet<PathWithRequiredFlags>();
+ mRequiredTransportFlags = 0;
+ mIsUsingNewScheme = false;
if (mFullBackupContent == 0 && mDataExtractionRules == 0) {
// No scheme specified via either new or legacy config, will copy everything.
@@ -535,12 +590,14 @@ public class FullBackup {
}
if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) {
// Found configuration in the new config, we will use it.
+ mIsUsingNewScheme = true;
return;
}
}
if (operationType == OperationType.MIGRATION
&& CompatChanges.isChangeEnabled(IGNORE_FULL_BACKUP_CONTENT_IN_D2D)) {
+ mIsUsingNewScheme = true;
return;
}
@@ -584,13 +641,24 @@ public class FullBackup {
continue;
}
- // TODO(b/180523028): Parse required attributes for rules (e.g. encryption).
+ parseRequiredTransportFlags(parser, configSection);
parseRules(parser, excludes, includes, Optional.of(0), configSection);
}
logParsingResults(excludes, includes);
}
+ private void parseRequiredTransportFlags(XmlPullParser parser,
+ @ConfigSection String configSection) {
+ if (ConfigSection.CLOUD_BACKUP.equals(configSection)) {
+ String encryptionAttribute = parser.getAttributeValue(/* namespace */ null,
+ FLAG_DISABLE_IF_NO_ENCRYPTION_CAPABILITIES);
+ if ("true".equals(encryptionAttribute)) {
+ mRequiredTransportFlags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+ }
+ }
+ }
+
@VisibleForTesting
public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
Set<PathWithRequiredFlags> excludes,
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 081f4fdc1b12..e6a4656bdbc5 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -334,10 +334,19 @@ public final class UsageEvents implements Parcelable {
public static final int LOCUS_ID_SET = 30;
/**
+ * An event type denoting that a component in the package has been used (e.g. broadcast
+ * receiver, service, content provider). This generally matches up with usage that would
+ * cause an app to leave force stop. The component itself is not provided as we are only
+ * interested in whether the package is used, not the component itself.
+ * @hide
+ */
+ public static final int APP_COMPONENT_USED = 31;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 30;
+ public static final int MAX_EVENT_TYPE = 31;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
index e3a130c4b436..4e64dbed7017 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
@@ -22,7 +22,7 @@ import android.os.Parcelable;
/**
* The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic
* advertising preferences for each Bluetooth LE advertising set. Use {@link
- * AdvertisingSetParameters.Builder} to create an instance of this class.
+ * PeriodicAdvertisingParameters.Builder} to create an instance of this class.
*/
public final class PeriodicAdvertisingParameters implements Parcelable {
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 102c98ff9329..17bdd42a0f45 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -60,6 +60,10 @@ public final class AssociationRequest implements Parcelable {
/**
* Device profile: watch.
*
+ * If specified, the current request may have a modified UI to highlight that the device being
+ * set up is a specific kind of device, and some extra permissions may be granted to the app
+ * as a result.
+ *
* @see AssociationRequest.Builder#setDeviceProfile
*/
public static final String DEVICE_PROFILE_WATCH =
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f3a4e1f79955..02e86cd4a863 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -370,6 +370,15 @@ public abstract class Context {
/*********** Hidden flags below this line ***********/
/**
+ * Flag for {@link #bindService}: This flag is only intended to be used by the system to
+ * indicate that a service binding is not considered as real package component usage and should
+ * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage
+ * stats.
+ * @hide
+ */
+ public static final int BIND_NOT_APP_COMPONENT_USAGE = 0x00008000;
+
+ /**
* Flag for {@link #bindService}: allow the process hosting the target service to be treated
* as if it's as important as a perceptible app to the user and avoid the oom killer killing
* this process in low memory situations until there aren't any other processes left but the
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0aa1be94d279..1a5dad5f7596 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1205,7 +1205,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
CATEGORY_SOCIAL,
CATEGORY_NEWS,
CATEGORY_MAPS,
- CATEGORY_PRODUCTIVITY
+ CATEGORY_PRODUCTIVITY,
+ CATEGORY_ACCESSIBILITY
})
@Retention(RetentionPolicy.SOURCE)
public @interface Category {
@@ -1281,6 +1282,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public static final int CATEGORY_PRODUCTIVITY = 7;
/**
+ * Category for apps which are primarily accessibility apps, such as screen-readers.
+ *
+ * @see #category
+ */
+ public static final int CATEGORY_ACCESSIBILITY = 8;
+
+ /**
* Return a concise, localized title for the given
* {@link ApplicationInfo#category} value, or {@code null} for unknown
* values such as {@link #CATEGORY_UNDEFINED}.
@@ -1305,6 +1313,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return context.getText(com.android.internal.R.string.app_category_maps);
case ApplicationInfo.CATEGORY_PRODUCTIVITY:
return context.getText(com.android.internal.R.string.app_category_productivity);
+ case ApplicationInfo.CATEGORY_ACCESSIBILITY:
+ return context.getText(com.android.internal.R.string.app_category_accessibility);
default:
return null;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b62f3b2f1a2..d79b66c1cf56 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7017,7 +7017,7 @@ public abstract class PackageManager {
* domain to an application, use
* {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
* passing in all of the domains returned inside
- * {@link DomainVerificationManager#getDomainVerificationUserSelection(String)}.
+ * {@link DomainVerificationManager#getDomainVerificationUserState(String)}.
*
* @hide
*/
diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.java b/core/java/android/content/pm/verify/domain/DomainOwner.java
index b050f5da7928..5bf2c0983a9a 100644
--- a/core/java/android/content/pm/verify/domain/DomainOwner.java
+++ b/core/java/android/content/pm/verify/domain/DomainOwner.java
@@ -66,16 +66,7 @@ public final class DomainOwner implements Parcelable {
* @param packageName
* Package name of that owns the domain.
* @param overrideable
- * Whether or not this owner can be automatically overridden. If all owners for a domain are
- * overrideable, then calling
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
- * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
- * of the owners are non-overrideable, then
- * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
- * boolean)} must be called with false to disable all of the other owners before this domain can
- * be taken by a new owner through
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
- * Set, boolean)}.
+ * Whether or not this owner can be automatically overridden.
*/
@DataClass.Generated.Member
public DomainOwner(
@@ -98,16 +89,9 @@ public final class DomainOwner implements Parcelable {
}
/**
- * Whether or not this owner can be automatically overridden. If all owners for a domain are
- * overrideable, then calling
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
- * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
- * of the owners are non-overrideable, then
- * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
- * boolean)} must be called with false to disable all of the other owners before this domain can
- * be taken by a new owner through
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
- * Set, boolean)}.
+ * Whether or not this owner can be automatically overridden.
+ *
+ * @see DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)
*/
@DataClass.Generated.Member
public boolean isOverrideable() {
@@ -205,7 +189,7 @@ public final class DomainOwner implements Parcelable {
};
@DataClass.Generated(
- time = 1614119379978L,
+ time = 1614721802044L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java",
inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final boolean mOverrideable\nclass DomainOwner extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genEqualsHashCode=true, genAidl=true, genToString=true)")
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 809587524f58..7c335b1d26dd 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -94,7 +94,7 @@ public final class DomainVerificationInfo implements Parcelable {
private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
- DomainVerificationUserSelection.class.getClassLoader());
+ DomainVerificationUserState.class.getClassLoader());
}
@@ -105,8 +105,7 @@ public final class DomainVerificationInfo implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
- // /DomainVerificationInfo.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -321,7 +320,7 @@ public final class DomainVerificationInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1613002530369L,
+ time = 1614721812023L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index 11402afac8b6..f7c81bcffda3 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -25,6 +25,8 @@ import android.annotation.SystemService;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import java.util.List;
@@ -32,55 +34,63 @@ import java.util.Set;
import java.util.UUID;
/**
- * System service to access the domain verification APIs.
+ * System service to access domain verification APIs.
*
- * Allows the approved domain verification
- * agent on the device (the sole holder of
- * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status
- * of domains declared by applications in their AndroidManifest.xml, to allow them to open those
- * links inside the app when selected by the user. This is done through querying
- * {@link #getDomainVerificationInfo(String)} and calling
- * {@link #setDomainVerificationStatus(UUID, Set, int)}.
- *
- * Also allows the domain preference settings (holder of
- * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the
- * preferences of the user, when they have chosen to explicitly allow an application to open links.
- * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling
- * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
- * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
- *
- * @hide
+ * Applications should use {@link #getDomainVerificationUserState(String)} if necessary to
+ * check if/how they are verified for a domain, which is required starting from platform
+ * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare
+ * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against
+ * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an
+ * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of
+ * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the
+ * caller when using {@link Context#startActivity(Intent)} and similar.
*/
-@SystemApi
@SystemService(Context.DOMAIN_VERIFICATION_SERVICE)
-public interface DomainVerificationManager {
+public final class DomainVerificationManager {
/**
- * Extra field name for a {@link DomainVerificationRequest} for the requested packages.
- * Passed to an the domain verification agent that handles
+ * Extra field name for a {@link DomainVerificationRequest} for the requested packages. Passed
+ * to an the domain verification agent that handles
* {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}.
+ *
+ * @hide
*/
- String EXTRA_VERIFICATION_REQUEST =
+ @SystemApi
+ public static final String EXTRA_VERIFICATION_REQUEST =
"android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
/**
* No response has been recorded by either the system or any verification agent.
+ *
+ * @hide
*/
- int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+ @SystemApi
+ public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
- /** The verification agent has explicitly verified the domain at some point. */
- int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+ /**
+ * The verification agent has explicitly verified the domain at some point.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
/**
- * The first available custom response code. This and any greater integer, along with
- * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values
- * will be treated as if the domain is unverified.
+ * The first available custom response code. This and any greater integer, along with {@link
+ * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be
+ * treated as if the domain is unverified.
+ *
+ * @hide
*/
- int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ @SystemApi
+ public static final int STATE_FIRST_VERIFIER_DEFINED =
+ DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
- /** @hide */
+ /**
+ * @hide
+ */
@NonNull
- static String stateToDebugString(@DomainVerificationState.State int state) {
+ public static String stateToDebugString(@DomainVerificationState.State int state) {
switch (state) {
case DomainVerificationState.STATE_NO_RESPONSE:
return "none";
@@ -104,10 +114,13 @@ public interface DomainVerificationManager {
}
/**
- * Checks if a state considers the corresponding domain to be successfully verified. The
- * domain verification agent may use this to determine whether or not to re-verify a domain.
+ * Checks if a state considers the corresponding domain to be successfully verified. The domain
+ * verification agent may use this to determine whether or not to re-verify a domain.
+ *
+ * @hide
*/
- static boolean isStateVerified(@DomainVerificationState.State int state) {
+ @SystemApi
+ public static boolean isStateVerified(@DomainVerificationState.State int state) {
switch (state) {
case DomainVerificationState.STATE_SUCCESS:
case DomainVerificationState.STATE_APPROVED:
@@ -126,10 +139,13 @@ public interface DomainVerificationManager {
/**
* Checks if a state is modifiable by the domain verification agent. This is useful as the
* platform may add new state codes in newer versions, and older verification agents can use
- * this method to determine if a state can be changed without having to be aware of what the
- * new state means.
+ * this method to determine if a state can be changed without having to be aware of what the new
+ * state means.
+ *
+ * @hide
*/
- static boolean isStateModifiable(@DomainVerificationState.State int state) {
+ @SystemApi
+ public static boolean isStateModifiable(@DomainVerificationState.State int state) {
switch (state) {
case DomainVerificationState.STATE_NO_RESPONSE:
case DomainVerificationState.STATE_SUCCESS:
@@ -147,11 +163,12 @@ public interface DomainVerificationManager {
}
/**
- * For determine re-verify policy. This is hidden from the domain verification agent so that
- * no behavior is made based on the result.
+ * For determine re-verify policy. This is hidden from the domain verification agent so that no
+ * behavior is made based on the result.
+ *
* @hide
*/
- static boolean isStateDefault(@DomainVerificationState.State int state) {
+ public static boolean isStateDefault(@DomainVerificationState.State int state) {
switch (state) {
case DomainVerificationState.STATE_NO_RESPONSE:
case DomainVerificationState.STATE_MIGRATED:
@@ -168,14 +185,72 @@ public interface DomainVerificationManager {
}
/**
+ * @hide
+ */
+ public static final int ERROR_INVALID_DOMAIN_SET = 1;
+ /**
+ * @hide
+ */
+ public static final int ERROR_NAME_NOT_FOUND = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"ERROR_"}, value = {
+ ERROR_INVALID_DOMAIN_SET,
+ ERROR_NAME_NOT_FOUND,
+ })
+ private @interface Error {
+ }
+
+ private final Context mContext;
+
+ private final IDomainVerificationManager mDomainVerificationManager;
+
+
+ /**
+ * System service to access the domain verification APIs.
+ * <p>
+ * Allows the approved domain verification agent on the device (the sole holder of {@link
+ * android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status of
+ * domains declared by applications in their AndroidManifest.xml, to allow them to open those
+ * links inside the app when selected by the user. This is done through querying {@link
+ * #getDomainVerificationInfo(String)} and calling {@link #setDomainVerificationStatus(UUID,
+ * Set, int)}.
+ * <p>
+ * Also allows the domain preference settings (holder of
+ * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION})
+ * to update the preferences of the user, when they have chosen to explicitly allow an
+ * application to open links. This is done through querying
+ * {@link #getDomainVerificationUserState(String)} and calling
+ * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
+ * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+ *
+ * @hide
+ */
+ public DomainVerificationManager(Context context,
+ IDomainVerificationManager domainVerificationManager) {
+ mContext = context;
+ mDomainVerificationManager = domainVerificationManager;
+ }
+
+ /**
* Used to iterate all {@link DomainVerificationInfo} values to do cleanup or retries. This is
* usually a heavy workload and should be done infrequently.
*
* @return the current snapshot of package names with valid autoVerify URLs.
+ * @hide
*/
+ @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- List<String> getValidVerificationPackageNames();
+ public List<String> queryValidVerificationPackageNames() {
+ try {
+ return mDomainVerificationManager.queryValidVerificationPackageNames();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Retrieves the domain verification state for a given package.
@@ -183,61 +258,106 @@ public interface DomainVerificationManager {
* @return the data for the package, or null if it does not declare any autoVerify domains
* @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error
* and should not be re-tried except on a time scheduled basis.
+ * @hide
*/
+ @SystemApi
@Nullable
@RequiresPermission(anyOf = {
android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
})
- DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
- throws NameNotFoundException;
+ public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+ throws NameNotFoundException {
+ try {
+ return mDomainVerificationManager.getDomainVerificationInfo(packageName);
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
/**
- * Change the verification status of the {@param domains} of the package associated with
- * {@param domainSetId}.
+ * Change the verification status of the {@param domains} of the package associated with {@param
+ * domainSetId}.
*
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains List of host names to change the state of.
* @param state See {@link DomainVerificationInfo#getHostToStateMap()}.
* @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
* invalid. This usually means the work being processed by the
- * verification agent is outdated and a new request should
- * be scheduled, if one has not already been done as part of
- * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}
- * broadcast.
+ * verification agent is outdated and a new request should be
+ * scheduled, if one has not already been done as part of the
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
* @throws NameNotFoundException If the ID is known to be good, but the package is
- * unavailable. This may be because the package is
- * installed on a volume that is no longer mounted. This
- * error is unrecoverable until the package is available
- * again, and should not be re-tried except on a time
- * scheduled basis.
+ * unavailable. This may be because the package is installed on
+ * a volume that is no longer mounted. This error is
+ * unrecoverable until the package is available again, and
+ * should not be re-tried except on a time scheduled basis.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- @DomainVerificationState.State int state) throws NameNotFoundException;
+ public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ @DomainVerificationState.State int state) throws NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+ new DomainSet(domains), state);
+ } catch (Exception e) {
+ Exception converted = rethrow(e, domainSetId);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
/**
- * TODO(b/178525735): This documentation is incorrect in the context of UX changes.
- * Change whether the given {@param packageName} is allowed to automatically open verified
- * HTTP/HTTPS domains. The final state is determined along with the verification status for the
- * specific domain being opened and other system state. An app with this enabled is not
- * guaranteed to be the sole link handler for its domains.
+ * Change whether the given packageName is allowed to handle BROWSABLE and DEFAULT category web
+ * (HTTP/HTTPS) {@link Intent} Activity open requests. The final state is determined along with
+ * the verification status for the specific domain being opened and other system state. An app
+ * with this enabled is not guaranteed to be the sole link handler for its domains.
+ * <p>
+ * By default, all apps are allowed to open links. Users must disable them explicitly.
*
- * By default, all apps are allowed to open verified links. Users must disable them explicitly.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed)
- throws NameNotFoundException;
+ public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+ boolean allowed) throws NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
+ allowed, mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
/**
* Update the recorded user selection for the given {@param domains} for the given {@param
* domainSetId}. This state is recorded for the lifetime of a domain for a package on device,
* and will never be reset by the system short of an app data clear.
- *
+ * <p>
* This state is stored per device user. If another user needs to be changed, the appropriate
- * permissions must be acquired and
- * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
- *
+ * permissions must be acquired and {@link Context#createContextAsUser(UserHandle, int)} should
+ * be used.
+ * <p>
* Enabling an unverified domain will allow an application to open it, but this can only occur
* if no other app on the device is approved for a higher approval level. This can queried
* using {@link #getOwnersForDomain(String)}.
@@ -255,33 +375,55 @@ public interface DomainVerificationManager {
* @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
* invalid.
* @throws NameNotFoundException If the ID is known to be good, but the package is
- * unavailable. This may be because the package is
- * installed on a volume that is no longer mounted. This
- * error is unrecoverable until the package is available
- * again, and should not be re-tried except on a time
- * scheduled basis.
+ * unavailable. This may be because the package is installed on
+ * a volume that is no longer mounted. This error is
+ * unrecoverable until the package is available again, and
+ * should not be re-tried except on a time scheduled basis.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException;
+ public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
+ new DomainSet(domains), enabled, mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, domainSetId);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
/**
* Retrieve the user selection data for the given {@param packageName} and the current user.
- * It is the responsibility of the caller to ensure that the
- * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls.
- *
- * This state is stored per device user. If another user needs to be accessed, the appropriate
- * permissions must be acquired and
- * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
*
* @param packageName The app to query state for.
- * @return the user selection verification data for the given package for the current user,
- * or null if the package does not declare any HTTP/HTTPS domains.
+ * @return the user selection verification data for the given package for the current user, or
+ * null if the package does not declare any HTTP/HTTPS domains.
*/
@Nullable
- @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String packageName)
- throws NameNotFoundException;
+ public DomainVerificationUserState getDomainVerificationUserState(
+ @NonNull String packageName) throws NameNotFoundException {
+ try {
+ return mDomainVerificationManager.getDomainVerificationUserState(packageName,
+ mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
/**
* For the given domain, return all apps which are approved to open it in a
@@ -291,21 +433,65 @@ public interface DomainVerificationManager {
*
* By default the list will be returned ordered from lowest to highest
* priority.
+ *
+ * @hide
*/
+ @SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- List<DomainOwner> getOwnersForDomain(@NonNull String domain);
+ public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
+ try {
+ return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
+ return rethrow(exception, domainSetId, null);
+ }
+
+ private Exception rethrow(Exception exception, @Nullable String packageName) {
+ return rethrow(exception, null, packageName);
+ }
+
+ private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
+ @Nullable String packageName) {
+ if (exception instanceof ServiceSpecificException) {
+ int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+ if (packageName == null) {
+ packageName = exception.getMessage();
+ }
+
+ @Error int managerErrorCode = packedErrorCode & 0xFFFF;
+ switch (managerErrorCode) {
+ case ERROR_INVALID_DOMAIN_SET:
+ int errorSpecificCode = packedErrorCode >> 16;
+ return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
+ domainSetId, packageName, errorSpecificCode));
+ case ERROR_NAME_NOT_FOUND:
+ return new NameNotFoundException(packageName);
+ default:
+ return exception;
+ }
+ } else if (exception instanceof RemoteException) {
+ return ((RemoteException) exception).rethrowFromSystemServer();
+ } else {
+ return exception;
+ }
+ }
/**
* Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
* provided by the caller is no longer valid. This may be recoverable, and the caller should
* re-query the package name associated with the ID using
- * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the
- * package is no longer known to the device and thus all pending work for it should be dropped.
+ * {@link #getDomainVerificationInfo(String)}
+ * in order to check. If that also fails, then the package is no longer known to the device and
+ * thus all pending work for it should be dropped.
*
* @hide
*/
- class InvalidDomainSetException extends IllegalArgumentException {
+ public static class InvalidDomainSetException extends IllegalArgumentException {
public static final int REASON_ID_NULL = 1;
public static final int REASON_ID_INVALID = 2;
@@ -313,7 +499,9 @@ public interface DomainVerificationManager {
public static final int REASON_UNKNOWN_DOMAIN = 4;
public static final int REASON_UNABLE_TO_APPROVE = 5;
- /** @hide */
+ /**
+ * @hide
+ */
@IntDef({
REASON_ID_NULL,
REASON_ID_INVALID,
@@ -352,7 +540,9 @@ public interface DomainVerificationManager {
@Nullable
private final String mPackageName;
- /** @hide */
+ /**
+ * @hide
+ */
public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
@Reason int reason) {
super(buildMessage(domainSetId, packageName, reason));
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
deleted file mode 100644
index 8b9865c2b436..000000000000
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
+++ /dev/null
@@ -1,202 +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 android.content.pm.verify.domain;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * @hide
- */
-@SuppressWarnings("RedundantThrows")
-public class DomainVerificationManagerImpl implements DomainVerificationManager {
-
- public static final int ERROR_INVALID_DOMAIN_SET = 1;
- public static final int ERROR_NAME_NOT_FOUND = 2;
-
- @IntDef(prefix = { "ERROR_" }, value = {
- ERROR_INVALID_DOMAIN_SET,
- ERROR_NAME_NOT_FOUND,
- })
- private @interface Error {
- }
-
- private final Context mContext;
-
- private final IDomainVerificationManager mDomainVerificationManager;
-
- public DomainVerificationManagerImpl(Context context,
- IDomainVerificationManager domainVerificationManager) {
- mContext = context;
- mDomainVerificationManager = domainVerificationManager;
- }
-
- @NonNull
- @Override
- public List<String> getValidVerificationPackageNames() {
- try {
- return mDomainVerificationManager.getValidVerificationPackageNames();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- @Nullable
- @Override
- public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
- throws NameNotFoundException {
- try {
- return mDomainVerificationManager.getDomainVerificationInfo(packageName);
- } catch (Exception e) {
- Exception converted = rethrow(e, packageName);
- if (converted instanceof NameNotFoundException) {
- throw (NameNotFoundException) converted;
- } else if (converted instanceof RuntimeException) {
- throw (RuntimeException) converted;
- } else {
- throw new RuntimeException(converted);
- }
- }
- }
-
- @Override
- public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- int state) throws IllegalArgumentException, NameNotFoundException {
- try {
- mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
- new DomainSet(domains), state);
- } catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
- if (converted instanceof NameNotFoundException) {
- throw (NameNotFoundException) converted;
- } else if (converted instanceof RuntimeException) {
- throw (RuntimeException) converted;
- } else {
- throw new RuntimeException(converted);
- }
- }
- }
-
- @Override
- public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
- boolean allowed) throws NameNotFoundException {
- try {
- mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
- allowed, mContext.getUserId());
- } catch (Exception e) {
- Exception converted = rethrow(e, packageName);
- if (converted instanceof NameNotFoundException) {
- throw (NameNotFoundException) converted;
- } else if (converted instanceof RuntimeException) {
- throw (RuntimeException) converted;
- } else {
- throw new RuntimeException(converted);
- }
- }
- }
-
- @Override
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean enabled)
- throws IllegalArgumentException, NameNotFoundException {
- try {
- mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
- new DomainSet(domains), enabled, mContext.getUserId());
- } catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
- if (converted instanceof NameNotFoundException) {
- throw (NameNotFoundException) converted;
- } else if (converted instanceof RuntimeException) {
- throw (RuntimeException) converted;
- } else {
- throw new RuntimeException(converted);
- }
- }
- }
-
- @Nullable
- @Override
- public DomainVerificationUserSelection getDomainVerificationUserSelection(
- @NonNull String packageName) throws NameNotFoundException {
- try {
- return mDomainVerificationManager.getDomainVerificationUserSelection(packageName,
- mContext.getUserId());
- } catch (Exception e) {
- Exception converted = rethrow(e, packageName);
- if (converted instanceof NameNotFoundException) {
- throw (NameNotFoundException) converted;
- } else if (converted instanceof RuntimeException) {
- throw (RuntimeException) converted;
- } else {
- throw new RuntimeException(converted);
- }
- }
- }
-
- @NonNull
- @Override
- public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
- try {
- return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
- return rethrow(exception, domainSetId, null);
- }
-
- private Exception rethrow(Exception exception, @Nullable String packageName) {
- return rethrow(exception, null, packageName);
- }
-
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
- @Nullable String packageName) {
- if (exception instanceof ServiceSpecificException) {
- int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
- if (packageName == null) {
- packageName = exception.getMessage();
- }
-
- @Error int managerErrorCode = packedErrorCode & 0xFFFF;
- switch (managerErrorCode) {
- case ERROR_INVALID_DOMAIN_SET:
- int errorSpecificCode = packedErrorCode >> 16;
- return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
- domainSetId, packageName, errorSpecificCode));
- case ERROR_NAME_NOT_FOUND:
- return new NameNotFoundException(packageName);
- default:
- return exception;
- }
- } else if (exception instanceof RemoteException) {
- return ((RemoteException) exception).rethrowFromSystemServer();
- } else {
- return exception;
- }
- }
-}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl
index ddb5ef85382a..94690c1dae93 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl
@@ -16,4 +16,4 @@
package android.content.pm.verify.domain;
-parcelable DomainVerificationUserSelection;
+parcelable DomainVerificationUserState;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
index d23f5f133841..1e60abb30011 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -29,39 +30,36 @@ import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
import java.util.Map;
-import java.util.Set;
import java.util.UUID;
/**
* Contains the user selection state for a package. This means all web HTTP(S) domains declared by a
* package in its manifest, whether or not they were marked for auto verification.
* <p>
- * By default, all apps are allowed to automatically open links with domains that they've
- * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. The user
- * can decide to disable this, disallowing the application from opening all links. Note that the
- * toggle affects <b>all</b> links and is not based on the verification state of the domains.
+ * Applications should use {@link #getHostToStateMap()} if necessary to
+ * check if/how they are verified for a domain, which is required starting from platform
+ * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare
+ * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against
+ * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an
+ * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of
+ * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the
+ * caller when using {@link Context#startActivity(Intent)} and similar.
+ * <p>
+ * By default, all apps are allowed to automatically open links for the above case for domains that
+ * they've successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}.
+ * The user can decide to disable this, disallowing the application from opening all links. Note
+ * that the toggle affects <b>all</b> links and is not based on the verification state of the
+ * domains.
* <p>
* Assuming the toggle is enabled, the user can also select additional unverified domains to grant
* to the application to open, which is reflected in {@link #getHostToStateMap()}. But only a single
* application can be approved for a domain unless the applications are both approved. If another
* application is approved, the user will not be allowed to enable the domain.
- * <p>
- * These values can be changed through the
- * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
- * boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
- * boolean)} APIs.
- * <p>
- * Note that because state is per user, if a different user needs to be changed, one will need to
- * use {@link Context#createContextAsUser(UserHandle, int)} and hold the {@link
- * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
- *
- * @hide
*/
-@SystemApi
@SuppressWarnings("DefaultAnnotationParam")
@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
genEqualsHashCode = true, genHiddenConstDefs = true)
-public final class DomainVerificationUserSelection implements Parcelable {
+public final class DomainVerificationUserState implements Parcelable {
/**
* The domain is unverified and unselected, and the application is unable to open web links
@@ -70,9 +68,8 @@ public final class DomainVerificationUserSelection implements Parcelable {
public static final int DOMAIN_STATE_NONE = 0;
/**
- * The domain has been selected through the
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)}
- * API, under the assumption it has not been reset by the system.
+ * The domain has been selected by the user. This may be reset to {@link #DOMAIN_STATE_NONE} if
+ * another application is selected or verified for the same domain.
*/
public static final int DOMAIN_STATE_SELECTED = 1;
@@ -119,7 +116,16 @@ public final class DomainVerificationUserSelection implements Parcelable {
@NonNull
private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
- DomainVerificationUserSelection.class.getClassLoader());
+ DomainVerificationUserState.class.getClassLoader());
+ }
+
+ /**
+ * @see DomainVerificationInfo#getIdentifier
+ * @hide
+ */
+ @SystemApi
+ public @NonNull UUID getIdentifier() {
+ return mIdentifier;
}
@@ -130,7 +136,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -162,7 +168,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
}
/**
- * Creates a new DomainVerificationUserSelection.
+ * Creates a new DomainVerificationUserState.
*
* @param packageName
* The package name that this data corresponds to.
@@ -175,7 +181,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
* @hide
*/
@DataClass.Generated.Member
- public DomainVerificationUserSelection(
+ public DomainVerificationUserState(
@NonNull UUID identifier,
@NonNull String packageName,
@NonNull UserHandle user,
@@ -201,14 +207,6 @@ public final class DomainVerificationUserSelection implements Parcelable {
}
/**
- * @see DomainVerificationInfo#getIdentifier
- */
- @DataClass.Generated.Member
- public @NonNull UUID getIdentifier() {
- return mIdentifier;
- }
-
- /**
* The package name that this data corresponds to.
*/
@DataClass.Generated.Member
@@ -246,7 +244,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
- return "DomainVerificationUserSelection { " +
+ return "DomainVerificationUserState { " +
"identifier = " + mIdentifier + ", " +
"packageName = " + mPackageName + ", " +
"user = " + mUser + ", " +
@@ -259,13 +257,13 @@ public final class DomainVerificationUserSelection implements Parcelable {
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(DomainVerificationUserSelection other) { ... }
+ // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
- DomainVerificationUserSelection that = (DomainVerificationUserSelection) o;
+ DomainVerificationUserState that = (DomainVerificationUserState) o;
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mIdentifier, that.mIdentifier)
@@ -323,7 +321,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ DomainVerificationUserSelection(@NonNull Parcel in) {
+ /* package-private */ DomainVerificationUserState(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -354,24 +352,24 @@ public final class DomainVerificationUserSelection implements Parcelable {
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<DomainVerificationUserSelection> CREATOR
- = new Parcelable.Creator<DomainVerificationUserSelection>() {
+ public static final @NonNull Parcelable.Creator<DomainVerificationUserState> CREATOR
+ = new Parcelable.Creator<DomainVerificationUserState>() {
@Override
- public DomainVerificationUserSelection[] newArray(int size) {
- return new DomainVerificationUserSelection[size];
+ public DomainVerificationUserState[] newArray(int size) {
+ return new DomainVerificationUserState[size];
}
@Override
- public DomainVerificationUserSelection createFromParcel(@NonNull Parcel in) {
- return new DomainVerificationUserSelection(in);
+ public DomainVerificationUserState createFromParcel(@NonNull Parcel in) {
+ return new DomainVerificationUserState(in);
}
};
@DataClass.Generated(
- time = 1613683603297L,
+ time = 1614721840152L,
codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java",
- inputSignatures = "public static final int DOMAIN_STATE_NONE\npublic static final int DOMAIN_STATE_SELECTED\npublic static final int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
+ sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java",
+ inputSignatures = "public static final int DOMAIN_STATE_NONE\npublic static final int DOMAIN_STATE_SELECTED\npublic static final int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\npublic @android.annotation.SystemApi @android.annotation.NonNull java.util.UUID getIdentifier()\nclass DomainVerificationUserState extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 701af320fb01..332b92544581 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -19,7 +19,7 @@ package android.content.pm.verify.domain;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
import java.util.List;
/**
@@ -28,13 +28,13 @@ import java.util.List;
*/
interface IDomainVerificationManager {
- List<String> getValidVerificationPackageNames();
+ List<String> queryValidVerificationPackageNames();
@nullable
DomainVerificationInfo getDomainVerificationInfo(String packageName);
@nullable
- DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
+ DomainVerificationUserState getDomainVerificationUserState(String packageName,
int userId);
@nullable
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index fd98d37bb7f4..31d1b69182f1 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -62,10 +62,13 @@ public interface BiometricAuthenticator {
* @hide
*/
int TYPE_FACE = 1 << 3;
- @IntDef({TYPE_NONE,
+
+ @IntDef(flag = true, value = {
+ TYPE_NONE,
TYPE_CREDENTIAL,
TYPE_FINGERPRINT,
- TYPE_IRIS})
+ TYPE_IRIS
+ })
@Retention(RetentionPolicy.SOURCE)
@interface Modality {}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 5b28e0035b09..1fdce5e773b1 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -23,6 +23,7 @@ import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -193,15 +194,15 @@ public class BiometricManager {
int DEVICE_CREDENTIAL = 1 << 15;
}
- private final Context mContext;
- private final IAuthService mService;
+ @NonNull private final Context mContext;
+ @NonNull private final IAuthService mService;
/**
* @hide
* @param context
* @param service
*/
- public BiometricManager(Context context, IAuthService service) {
+ public BiometricManager(@NonNull Context context, @NonNull IAuthService service) {
mContext = context;
mService = service;
}
@@ -274,7 +275,8 @@ public class BiometricManager {
*/
@Deprecated
@RequiresPermission(USE_BIOMETRIC)
- public @BiometricError int canAuthenticate() {
+ @BiometricError
+ public int canAuthenticate() {
return canAuthenticate(Authenticators.BIOMETRIC_WEAK);
}
@@ -304,7 +306,8 @@ public class BiometricManager {
* authenticators can currently be used (enrolled and available).
*/
@RequiresPermission(USE_BIOMETRIC)
- public @BiometricError int canAuthenticate(@Authenticators.Types int authenticators) {
+ @BiometricError
+ public int canAuthenticate(@Authenticators.Types int authenticators) {
return canAuthenticate(mContext.getUserId(), authenticators);
}
@@ -312,8 +315,10 @@ public class BiometricManager {
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public @BiometricError int canAuthenticate(int userId,
- @Authenticators.Types int authenticators) {
+ @BiometricError
+ public int canAuthenticate(
+ int userId, @Authenticators.Types int authenticators) {
+
if (mService != null) {
try {
final String opPackageName = mContext.getOpPackageName();
@@ -322,7 +327,7 @@ public class BiometricManager {
throw e.rethrowFromSystemServer();
}
} else {
- Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+ Slog.w(TAG, "canAuthenticate(): Service not connected");
return BIOMETRIC_ERROR_HW_UNAVAILABLE;
}
}
@@ -404,5 +409,115 @@ public class BiometricManager {
}
}
+ /**
+ * Provides a localized string that may be used as the label for a button that invokes
+ * {@link BiometricPrompt}.
+ *
+ * <p>When possible, this method should use the given authenticator requirements to more
+ * precisely specify the authentication type that will be used. For example, if
+ * <strong>Class 3</strong> biometric authentication is requested on a device with a
+ * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+ * returned string should indicate that fingerprint authentication will be used.
+ *
+ * <p>This method should also try to specify which authentication method(s) will be used in
+ * practice when multiple authenticators meet the given requirements. For example, if biometric
+ * authentication is requested on a device with both face and fingerprint sensors but the user
+ * has selected face as their preferred method, the returned string should indicate that face
+ * authentication will be used.
+ *
+ * @param authenticators A bit field representing the types of {@link Authenticators} that may
+ * be used for authentication.
+ * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @Nullable
+ public CharSequence getButtonLabel(@Authenticators.Types int authenticators) {
+ if (mService != null) {
+ final int userId = mContext.getUserId();
+ final String opPackageName = mContext.getOpPackageName();
+ try {
+ return mService.getButtonLabel(userId, opPackageName, authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "getButtonLabel(): Service not connected");
+ return null;
+ }
+ }
+
+ /**
+ * Provides a localized string that may be shown while the user is authenticating with
+ * {@link BiometricPrompt}.
+ *
+ * <p>When possible, this method should use the given authenticator requirements to more
+ * precisely specify the authentication type that will be used. For example, if
+ * <strong>Class 3</strong> biometric authentication is requested on a device with a
+ * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+ * returned string should indicate that fingerprint authentication will be used.
+ *
+ * <p>This method should also try to specify which authentication method(s) will be used in
+ * practice when multiple authenticators meet the given requirements. For example, if biometric
+ * authentication is requested on a device with both face and fingerprint sensors but the user
+ * has selected face as their preferred method, the returned string should indicate that face
+ * authentication will be used.
+ *
+ * @param authenticators A bit field representing the types of {@link Authenticators} that may
+ * be used for authentication.
+ * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @Nullable
+ public CharSequence getPromptMessage(@Authenticators.Types int authenticators) {
+ if (mService != null) {
+ final int userId = mContext.getUserId();
+ final String opPackageName = mContext.getOpPackageName();
+ try {
+ return mService.getPromptMessage(userId, opPackageName, authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "getPromptMessage(): Service not connected");
+ return null;
+ }
+ }
+
+ /**
+ * Provides a localized string that may be shown as the title for an app setting that enables
+ * biometric authentication.
+ *
+ * <p>When possible, this method should use the given authenticator requirements to more
+ * precisely specify the authentication type that will be used. For example, if
+ * <strong>Class 3</strong> biometric authentication is requested on a device with a
+ * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+ * returned string should indicate that fingerprint authentication will be used.
+ *
+ * <p>This method should <em>not</em> try to specify which authentication method(s) will be used
+ * in practice when multiple authenticators meet the given requirements. For example, if
+ * biometric authentication is requested on a device with both face and fingerprint sensors, the
+ * returned string should indicate that either face or fingerprint authentication may be used,
+ * regardless of whether the user has enrolled or selected either as their preferred method.
+ *
+ * @param authenticators A bit field representing the types of {@link Authenticators} that may
+ * be used for authentication.
+ * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @Nullable
+ public CharSequence getSettingName(@Authenticators.Types int authenticators) {
+ if (mService != null) {
+ final int userId = mContext.getUserId();
+ final String opPackageName = mContext.getOpPackageName();
+ try {
+ return mService.getSettingName(userId, opPackageName, authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "getSettingName(): Service not connected");
+ return null;
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index d8c9dbc849a9..1472bb940be5 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -68,4 +68,16 @@ interface IAuthService {
// the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
// land as SIDs, and are used during key generation.
long[] getAuthenticatorIds();
+
+ // Provides a localized string that may be used as the label for a button that invokes
+ // BiometricPrompt.
+ CharSequence getButtonLabel(int userId, String opPackageName, int authenticators);
+
+ // Provides a localized string that may be shown while the user is authenticating with
+ // BiometricPrompt.
+ CharSequence getPromptMessage(int userId, String opPackageName, int authenticators);
+
+ // Provides a localized string that may be shown as the title for an app setting that enables
+ // biometric authentication.
+ CharSequence getSettingName(int userId, String opPackageName, int authenticators);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 24331863a05f..6d8bf0fb5543 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -75,4 +75,10 @@ interface IBiometricService {
long[] getAuthenticatorIds(int callingUserId);
int getCurrentStrength(int sensorId);
+
+ // Returns a bit field of the modality (or modalities) that are will be used for authentication.
+ int getCurrentModality(String opPackageName, int userId, int callingUserId, int authenticators);
+
+ // Returns a bit field of the authentication modalities that are supported by this device.
+ int getSupportedModalities(int authenticators);
}
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 06b5b6745bd1..a5c9a7fafbd8 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -34,10 +34,7 @@ import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
-import android.system.ErrnoException;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
@@ -111,13 +108,9 @@ class ConversionUtil {
aidlModel.type = apiModel.getType();
aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
- try {
- aidlModel.data = ParcelFileDescriptor.dup(
- byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- aidlModel.dataSize = apiModel.getData().length;
+ byte[] data = apiModel.getData();
+ aidlModel.data = byteArrayToSharedMemory(data, "SoundTrigger SoundModel");
+ aidlModel.dataSize = data.length;
return aidlModel;
}
@@ -379,7 +372,7 @@ class ConversionUtil {
return result;
}
- private static @Nullable FileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
+ private static @Nullable ParcelFileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
if (data.length == 0) {
return null;
}
@@ -389,8 +382,10 @@ class ConversionUtil {
ByteBuffer buffer = shmem.mapReadWrite();
buffer.put(data);
shmem.unmap(buffer);
- return shmem.getFileDescriptor();
- } catch (ErrnoException e) {
+ ParcelFileDescriptor fd = shmem.getFdDup();
+ shmem.close();
+ return fd;
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 183f500572bd..cc1312bac180 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -24,10 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.content.pm.PackageManager;
-import android.os.Process;
import android.security.Credentials;
-import android.security.KeyStore;
-import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnProfile;
@@ -35,7 +32,9 @@ import com.android.internal.net.VpnProfile;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
+import java.security.Key;
import java.security.KeyFactory;
+import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
@@ -66,6 +65,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
/** Prefix for when a Private Key is stored directly in the profile @hide */
public static final String PREFIX_INLINE = "INLINE:";
+ private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
private static final String EMPTY_CERT = "";
@@ -430,32 +430,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
return profile;
}
- /**
- * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
- *
- * <p>Redundant authentication information (not related to profile type) will be discarded.
- *
- * @hide
- */
- @NonNull
- public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
- throws IOException, GeneralSecurityException {
- return fromVpnProfile(profile, null);
+ private static PrivateKey getPrivateKeyFromAndroidKeystore(String alias) {
+ try {
+ final KeyStore keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER);
+ keystore.load(null);
+ final Key key = keystore.getKey(alias, null);
+ if (!(key instanceof PrivateKey)) {
+ throw new IllegalStateException(
+ "Unexpected key type returned from android keystore.");
+ }
+ return (PrivateKey) key;
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to load key from android keystore.", e);
+ }
}
/**
* Builds the Ikev2VpnProfile from the given profile.
*
* @param profile the source VpnProfile to build from
- * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
- * the private key is PEM-encoded into the profile.
* @return The IKEv2/IPsec VPN profile
* @hide
*/
@NonNull
- public static Ikev2VpnProfile fromVpnProfile(
- @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
- throws IOException, GeneralSecurityException {
+ public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+ throws GeneralSecurityException {
final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
builder.setProxy(profile.proxy);
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
@@ -479,12 +478,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
case TYPE_IKEV2_IPSEC_RSA:
final PrivateKey key;
if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
- Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");
-
final String alias =
profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
- key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- keyStore, alias, Process.myUid());
+ key = getPrivateKeyFromAndroidKeystore(alias);
} else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
} else {
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 8f1e2defd215..268002f1dd52 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,11 +232,10 @@ public final class IpSecAlgorithm implements Parcelable {
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
- // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
- ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
}
private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index b4034556f66e..48bd29769f83 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -29,7 +29,15 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-/** @hide */
+/**
+ * Network preferences to set the default active network on a per-application basis as per a given
+ * {@link OemNetworkPreference}. An example of this would be to set an application's network
+ * preference to {@link #OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK} which would have the default
+ * network for that application set to an unmetered network first if available and if not, it then
+ * set that application's default network to an OEM managed network if available.
+ *
+ * @hide
+ */
@SystemApi
public final class OemNetworkPreferences implements Parcelable {
/**
@@ -64,6 +72,10 @@ public final class OemNetworkPreferences implements Parcelable {
@NonNull
private final Bundle mNetworkMappings;
+ /**
+ * Return the currently built application package name to {@link OemNetworkPreference} mappings.
+ * @return the current network preferences map.
+ */
@NonNull
public Map<String, Integer> getNetworkPreferences() {
return convertToUnmodifiableMap(mNetworkMappings);
@@ -105,6 +117,11 @@ public final class OemNetworkPreferences implements Parcelable {
mNetworkMappings = new Bundle();
}
+ /**
+ * Constructor to populate the builder's values with an already built
+ * {@link OemNetworkPreferences}.
+ * @param preferences the {@link OemNetworkPreferences} to populate with.
+ */
public Builder(@NonNull final OemNetworkPreferences preferences) {
Objects.requireNonNull(preferences);
mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index d91cef592d10..236ae8bb11b2 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -18,7 +18,6 @@ package android.net.vcn;
/** @hide */
oneway interface IVcnStatusCallback {
- void onEnteredSafeMode();
void onVcnStatusChanged(int statusCode);
void onGatewayConnectionError(
in int[] gatewayNetworkCapabilities,
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index eb8c251fec78..8ebf757760c3 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -359,8 +359,6 @@ public class VcnManager {
/**
* Value indicating that the VCN for the subscription group is not configured, or that the
* callback is not privileged for the subscription group.
- *
- * @hide
*/
public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0;
@@ -369,8 +367,6 @@ public class VcnManager {
*
* <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the
* provisioning package is not privileged.
- *
- * @hide
*/
public static final int VCN_STATUS_CODE_INACTIVE = 1;
@@ -380,8 +376,6 @@ public class VcnManager {
* <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning
* package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered
* active while it is connecting, fully connected, and disconnecting.
- *
- * @hide
*/
public static final int VCN_STATUS_CODE_ACTIVE = 2;
@@ -391,8 +385,6 @@ public class VcnManager {
* <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to
* establish a connection within a system-determined timeout (while underlying networks were
* available).
- *
- * @hide
*/
public static final int VCN_STATUS_CODE_SAFE_MODE = 3;
@@ -407,8 +399,6 @@ public class VcnManager {
/**
* Value indicating that an internal failure occurred in this Gateway Connection.
- *
- * @hide
*/
public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0;
@@ -416,8 +406,6 @@ public class VcnManager {
* Value indicating that an error with this Gateway Connection's configuration occurred.
*
* <p>For example, this error code will be returned after authentication failures.
- *
- * @hide
*/
public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1;
@@ -427,38 +415,19 @@ public class VcnManager {
* <p>For example, this error code will be returned if an underlying {@link android.net.Network}
* for this Gateway Connection is lost, or if an error occurs while resolving the connection
* endpoint address.
- *
- * @hide
*/
public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2;
- // TODO: make VcnStatusCallback @SystemApi
/**
* VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
*
* <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a
* subscription group.
- *
- * @hide
*/
public abstract static class VcnStatusCallback {
private VcnStatusCallbackBinder mCbBinder;
/**
- * Invoked when the VCN for this Callback's subscription group enters safe mode.
- *
- * <p>A VCN will be put into safe mode if any of the gateway connections were unable to
- * establish a connection within a system-determined timeout (while underlying networks were
- * available).
- *
- * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
- * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
- *
- * @hide
- */
- public void onEnteredSafeMode() {}
-
- /**
* Invoked when status of the VCN for this callback's subscription group changes.
*
* @param statusCode the code for the status change encountered by this {@link
@@ -467,15 +436,16 @@ public class VcnManager {
public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
/**
- * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
+ * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group
* encounters an error.
*
- * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway
- * Connection that encountered the error for identification purposes. These will be a
- * sorted list with no duplicates, matching one of the {@link
+ * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities
+ * for the Gateway Connection that encountered the error, for identification purposes.
+ * These will be a sorted list with no duplicates and will match {@link
+ * VcnGatewayConnectionConfig#getRequiredUnderlyingCapabilities()} for one of the {@link
* VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
* group.
- * @param errorCode {@link VcnErrorCode} to indicate the error that occurred
+ * @param errorCode the code to indicate the error that occurred
* @param detail Throwable to provide additional information about the error, or {@code
* null} if none
*/
@@ -496,6 +466,10 @@ public class VcnManager {
* <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
* privileges for the specified subscription at the time of invocation.
*
+ * <p>A {@link VcnStatusCallback} is eligible to begin receiving callbacks once it is registered
+ * and there is a VCN active for its specified subscription group (this may happen after the
+ * callback is registered).
+ *
* <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
* current status for the specified subscription group's VCN. If the registrant is not
* privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
@@ -505,7 +479,6 @@ public class VcnManager {
* @param executor The {@link Executor} to be used for invoking callbacks
* @param callback The VcnStatusCallback to be registered
* @throws IllegalStateException if callback is currently registered with VcnManager
- * @hide
*/
public void registerVcnStatusCallback(
@NonNull ParcelUuid subscriptionGroup,
@@ -538,7 +511,6 @@ public class VcnManager {
* was registered with.
*
* @param callback The callback to be unregistered
- * @hide
*/
public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) {
requireNonNull(callback, "callback must not be null");
@@ -599,12 +571,6 @@ public class VcnManager {
}
@Override
- public void onEnteredSafeMode() {
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
- }
-
- @Override
public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
diff --git a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
new file mode 100644
index 000000000000..b6036b4a6fd1
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * CertUtils provides utility methods for constructing Certificate.
+ *
+ * @hide
+ */
+public class CertUtils {
+ private static final String CERT_TYPE_X509 = "X.509";
+
+ /** Decodes an ASN.1 DER encoded Certificate */
+ public static X509Certificate certificateFromByteArray(byte[] derEncoded) {
+ Objects.requireNonNull(derEncoded, "derEncoded is null");
+
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance(CERT_TYPE_X509);
+ InputStream in = new ByteArrayInputStream(derEncoded);
+ return (X509Certificate) certFactory.generateCertificate(in);
+ } catch (CertificateException e) {
+ throw new IllegalArgumentException("Fail to decode certificate", e);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
new file mode 100644
index 000000000000..ce5ec75f01a2
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert ChildSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class ChildSaProposalUtils extends SaProposalUtilsBase {
+ /** Serializes a ChildSaProposal to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(ChildSaProposal proposal) {
+ return SaProposalUtilsBase.toPersistableBundle(proposal);
+ }
+
+ /** Constructs a ChildSaProposal by deserializing a PersistableBundle. */
+ @NonNull
+ public static ChildSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final ChildSaProposal.Builder builder = new ChildSaProposal.Builder();
+
+ final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+ Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+ final List<EncryptionAlgoKeyLenPair> encryptList =
+ PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+ for (EncryptionAlgoKeyLenPair t : encryptList) {
+ builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+ }
+
+ final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+ Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+ for (int algo : integrityAlgoIdArray) {
+ builder.addIntegrityAlgorithm(algo);
+ }
+
+ final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+ Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+ for (int dh : dhGroupArray) {
+ builder.addDhGroup(dh);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
new file mode 100644
index 000000000000..853a52da766a
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.eap.EapSessionConfig;
+import android.net.eap.EapSessionConfig.EapAkaConfig;
+import android.net.eap.EapSessionConfig.EapAkaPrimeConfig;
+import android.net.eap.EapSessionConfig.EapMethodConfig;
+import android.net.eap.EapSessionConfig.EapMsChapV2Config;
+import android.net.eap.EapSessionConfig.EapSimConfig;
+import android.net.eap.EapSessionConfig.EapTtlsConfig;
+import android.net.eap.EapSessionConfig.EapUiccConfig;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert EapSessionConfig to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class EapSessionConfigUtils {
+ private static final String EAP_ID_KEY = "EAP_ID_KEY";
+ private static final String EAP_SIM_CONFIG_KEY = "EAP_SIM_CONFIG_KEY";
+ private static final String EAP_TTLS_CONFIG_KEY = "EAP_TTLS_CONFIG_KEY";
+ private static final String EAP_AKA_CONFIG_KEY = "EAP_AKA_CONFIG_KEY";
+ private static final String EAP_MSCHAP_V2_CONFIG_KEY = "EAP_MSCHAP_V2_CONFIG_KEY";
+ private static final String EAP_AKA_PRIME_CONFIG_KEY = "EAP_AKA_PRIME_CONFIG_KEY";
+
+ /** Serializes an EapSessionConfig to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapSessionConfig config) {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putPersistableBundle(
+ EAP_ID_KEY, PersistableBundleUtils.fromByteArray(config.getEapIdentity()));
+
+ if (config.getEapSimConfig() != null) {
+ result.putPersistableBundle(
+ EAP_SIM_CONFIG_KEY,
+ EapSimConfigUtils.toPersistableBundle(config.getEapSimConfig()));
+ }
+
+ if (config.getEapTtlsConfig() != null) {
+ result.putPersistableBundle(
+ EAP_TTLS_CONFIG_KEY,
+ EapTtlsConfigUtils.toPersistableBundle(config.getEapTtlsConfig()));
+ }
+
+ if (config.getEapAkaConfig() != null) {
+ result.putPersistableBundle(
+ EAP_AKA_CONFIG_KEY,
+ EapAkaConfigUtils.toPersistableBundle(config.getEapAkaConfig()));
+ }
+
+ if (config.getEapMsChapV2Config() != null) {
+ result.putPersistableBundle(
+ EAP_MSCHAP_V2_CONFIG_KEY,
+ EapMsChapV2ConfigUtils.toPersistableBundle(config.getEapMsChapV2Config()));
+ }
+
+ if (config.getEapAkaPrimeConfig() != null) {
+ result.putPersistableBundle(
+ EAP_AKA_PRIME_CONFIG_KEY,
+ EapAkaPrimeConfigUtils.toPersistableBundle(config.getEapAkaPrimeConfig()));
+ }
+
+ return result;
+ }
+
+ /** Constructs an EapSessionConfig by deserializing a PersistableBundle. */
+ @NonNull
+ public static EapSessionConfig fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final EapSessionConfig.Builder builder = new EapSessionConfig.Builder();
+
+ final PersistableBundle eapIdBundle = in.getPersistableBundle(EAP_ID_KEY);
+ Objects.requireNonNull(eapIdBundle, "EAP ID was null");
+ builder.setEapIdentity(PersistableBundleUtils.toByteArray(eapIdBundle));
+
+ final PersistableBundle simBundle = in.getPersistableBundle(EAP_SIM_CONFIG_KEY);
+ if (simBundle != null) {
+ EapSimConfigUtils.setBuilderByReadingPersistableBundle(simBundle, builder);
+ }
+
+ final PersistableBundle ttlsBundle = in.getPersistableBundle(EAP_TTLS_CONFIG_KEY);
+ if (ttlsBundle != null) {
+ EapTtlsConfigUtils.setBuilderByReadingPersistableBundle(ttlsBundle, builder);
+ }
+
+ final PersistableBundle akaBundle = in.getPersistableBundle(EAP_AKA_CONFIG_KEY);
+ if (akaBundle != null) {
+ EapAkaConfigUtils.setBuilderByReadingPersistableBundle(akaBundle, builder);
+ }
+
+ final PersistableBundle msChapV2Bundle = in.getPersistableBundle(EAP_MSCHAP_V2_CONFIG_KEY);
+ if (msChapV2Bundle != null) {
+ EapMsChapV2ConfigUtils.setBuilderByReadingPersistableBundle(msChapV2Bundle, builder);
+ }
+
+ final PersistableBundle akaPrimeBundle = in.getPersistableBundle(EAP_AKA_PRIME_CONFIG_KEY);
+ if (akaPrimeBundle != null) {
+ EapAkaPrimeConfigUtils.setBuilderByReadingPersistableBundle(akaPrimeBundle, builder);
+ }
+
+ return builder.build();
+ }
+
+ private static class EapMethodConfigUtils {
+ private static final String METHOD_TYPE = "METHOD_TYPE";
+
+ /** Serializes an EapMethodConfig to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapMethodConfig config) {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(METHOD_TYPE, config.getMethodType());
+ return result;
+ }
+ }
+
+ private static class EapUiccConfigUtils extends EapMethodConfigUtils {
+ static final String SUB_ID_KEY = "SUB_ID_KEY";
+ static final String APP_TYPE_KEY = "APP_TYPE_KEY";
+
+ @NonNull
+ protected static PersistableBundle toPersistableBundle(@NonNull EapUiccConfig config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ result.putInt(SUB_ID_KEY, config.getSubId());
+ result.putInt(APP_TYPE_KEY, config.getAppType());
+
+ return result;
+ }
+ }
+
+ private static final class EapSimConfigUtils extends EapUiccConfigUtils {
+ @NonNull
+ public static PersistableBundle toPersistableBundle(EapSimConfig config) {
+ return EapUiccConfigUtils.toPersistableBundle(config);
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapSimConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+ }
+ }
+
+ private static class EapAkaConfigUtils extends EapUiccConfigUtils {
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapAkaConfig config) {
+ return EapUiccConfigUtils.toPersistableBundle(config);
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapAkaConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+ }
+ }
+
+ private static final class EapAkaPrimeConfigUtils extends EapAkaConfigUtils {
+ private static final String NETWORK_NAME_KEY = "NETWORK_NAME_KEY";
+ private static final String ALL_MISMATCHED_NETWORK_KEY = "ALL_MISMATCHED_NETWORK_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapAkaPrimeConfig config) {
+ final PersistableBundle result = EapUiccConfigUtils.toPersistableBundle(config);
+ result.putString(NETWORK_NAME_KEY, config.getNetworkName());
+ result.putBoolean(ALL_MISMATCHED_NETWORK_KEY, config.allowsMismatchedNetworkNames());
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapAkaPrimeConfig(
+ in.getInt(SUB_ID_KEY),
+ in.getInt(APP_TYPE_KEY),
+ in.getString(NETWORK_NAME_KEY),
+ in.getBoolean(ALL_MISMATCHED_NETWORK_KEY));
+ }
+ }
+
+ private static final class EapMsChapV2ConfigUtils extends EapMethodConfigUtils {
+ private static final String USERNAME_KEY = "USERNAME_KEY";
+ private static final String PASSWORD_KEY = "PASSWORD_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapMsChapV2Config config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ result.putString(USERNAME_KEY, config.getUsername());
+ result.putString(PASSWORD_KEY, config.getPassword());
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ builder.setEapMsChapV2Config(in.getString(USERNAME_KEY), in.getString(PASSWORD_KEY));
+ }
+ }
+
+ private static final class EapTtlsConfigUtils extends EapMethodConfigUtils {
+ private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
+ private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
+
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull EapTtlsConfig config) {
+ final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+ try {
+ if (config.getServerCaCert() != null) {
+ final PersistableBundle caBundle =
+ PersistableBundleUtils.fromByteArray(
+ config.getServerCaCert().getEncoded());
+ result.putPersistableBundle(TRUST_CERT_KEY, caBundle);
+ }
+ } catch (CertificateEncodingException e) {
+ throw new IllegalStateException("Fail to encode the certificate");
+ }
+
+ result.putPersistableBundle(
+ EAP_SESSION_CONFIG_KEY,
+ EapSessionConfigUtils.toPersistableBundle(config.getInnerEapSessionConfig()));
+
+ return result;
+ }
+
+ public static void setBuilderByReadingPersistableBundle(
+ @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final PersistableBundle caBundle = in.getPersistableBundle(TRUST_CERT_KEY);
+ X509Certificate caCert = null;
+ if (caBundle != null) {
+ caCert =
+ CertUtils.certificateFromByteArray(
+ PersistableBundleUtils.toByteArray(caBundle));
+ }
+
+ final PersistableBundle eapSessionConfigBundle =
+ in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
+ Objects.requireNonNull(eapSessionConfigBundle, "Inner EAP Session Config was null");
+ final EapSessionConfig eapSessionConfig =
+ EapSessionConfigUtils.fromPersistableBundle(eapSessionConfigBundle);
+
+ builder.setEapTtlsConfig(caCert, eapSessionConfig);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
new file mode 100644
index 000000000000..6acb34ebb78e
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Objects;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Abstract utility class to convert IkeIdentification to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeIdentificationUtils {
+ private static final String ID_TYPE_KEY = "ID_TYPE_KEY";
+
+ private static final String DER_ASN1_DN_KEY = "DER_ASN1_DN_KEY";
+ private static final String FQDN_KEY = "FQDN_KEY";
+ private static final String KEY_ID_KEY = "KEY_ID_KEY";
+ private static final String IP4_ADDRESS_KEY = "IP4_ADDRESS_KEY";
+ private static final String IP6_ADDRESS_KEY = "IP6_ADDRESS_KEY";
+ private static final String RFC822_ADDRESS_KEY = "RFC822_ADDRESS_KEY";
+
+ private static final int ID_TYPE_DER_ASN1_DN = 1;
+ private static final int ID_TYPE_FQDN = 2;
+ private static final int ID_TYPE_IPV4_ADDR = 3;
+ private static final int ID_TYPE_IPV6_ADDR = 4;
+ private static final int ID_TYPE_KEY_ID = 5;
+ private static final int ID_TYPE_RFC822_ADDR = 6;
+
+ /** Serializes an IkeIdentification to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull IkeIdentification ikeId) {
+ if (ikeId instanceof IkeDerAsn1DnIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_DER_ASN1_DN);
+ IkeDerAsn1DnIdentification id = (IkeDerAsn1DnIdentification) ikeId;
+ result.putPersistableBundle(
+ DER_ASN1_DN_KEY,
+ PersistableBundleUtils.fromByteArray(id.derAsn1Dn.getEncoded()));
+ return result;
+ } else if (ikeId instanceof IkeFqdnIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_FQDN);
+ IkeFqdnIdentification id = (IkeFqdnIdentification) ikeId;
+ result.putString(FQDN_KEY, id.fqdn);
+ return result;
+ } else if (ikeId instanceof IkeIpv4AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV4_ADDR);
+ IkeIpv4AddrIdentification id = (IkeIpv4AddrIdentification) ikeId;
+ result.putString(IP4_ADDRESS_KEY, id.ipv4Address.getHostAddress());
+ return result;
+ } else if (ikeId instanceof IkeIpv6AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV6_ADDR);
+ IkeIpv6AddrIdentification id = (IkeIpv6AddrIdentification) ikeId;
+ result.putString(IP6_ADDRESS_KEY, id.ipv6Address.getHostAddress());
+ return result;
+ } else if (ikeId instanceof IkeKeyIdIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_KEY_ID);
+ IkeKeyIdIdentification id = (IkeKeyIdIdentification) ikeId;
+ result.putPersistableBundle(KEY_ID_KEY, PersistableBundleUtils.fromByteArray(id.keyId));
+ return result;
+ } else if (ikeId instanceof IkeRfc822AddrIdentification) {
+ final PersistableBundle result = createPersistableBundle(ID_TYPE_RFC822_ADDR);
+ IkeRfc822AddrIdentification id = (IkeRfc822AddrIdentification) ikeId;
+ result.putString(RFC822_ADDRESS_KEY, id.rfc822Name);
+ return result;
+ } else {
+ throw new IllegalStateException("Unrecognized IkeIdentification subclass");
+ }
+ }
+
+ private static PersistableBundle createPersistableBundle(int idType) {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(ID_TYPE_KEY, idType);
+ return result;
+ }
+
+ /** Constructs an IkeIdentification by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeIdentification fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+ int idType = in.getInt(ID_TYPE_KEY);
+ switch (idType) {
+ case ID_TYPE_DER_ASN1_DN:
+ final PersistableBundle dnBundle = in.getPersistableBundle(DER_ASN1_DN_KEY);
+ Objects.requireNonNull(dnBundle, "ASN1 DN was null");
+ return new IkeDerAsn1DnIdentification(
+ new X500Principal(PersistableBundleUtils.toByteArray(dnBundle)));
+ case ID_TYPE_FQDN:
+ return new IkeFqdnIdentification(in.getString(FQDN_KEY));
+ case ID_TYPE_IPV4_ADDR:
+ final String v4AddressStr = in.getString(IP4_ADDRESS_KEY);
+ Objects.requireNonNull(v4AddressStr, "IPv4 address was null");
+ return new IkeIpv4AddrIdentification(
+ (Inet4Address) InetAddresses.parseNumericAddress(v4AddressStr));
+ case ID_TYPE_IPV6_ADDR:
+ final String v6AddressStr = in.getString(IP6_ADDRESS_KEY);
+ Objects.requireNonNull(v6AddressStr, "IPv6 address was null");
+ return new IkeIpv6AddrIdentification(
+ (Inet6Address) InetAddresses.parseNumericAddress(v6AddressStr));
+ case ID_TYPE_KEY_ID:
+ final PersistableBundle keyIdBundle = in.getPersistableBundle(KEY_ID_KEY);
+ Objects.requireNonNull(in, "Key ID was null");
+ return new IkeKeyIdIdentification(PersistableBundleUtils.toByteArray(keyIdBundle));
+ case ID_TYPE_RFC822_ADDR:
+ return new IkeRfc822AddrIdentification(in.getString(RFC822_ADDRESS_KEY));
+ default:
+ throw new IllegalStateException("Unrecognized IKE ID type: " + idType);
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
new file mode 100644
index 000000000000..1459671f4136
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert IkeSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeSaProposalUtils extends SaProposalUtilsBase {
+ private static final String PRF_KEY = "PRF_KEY";
+
+ /** Serializes an IkeSaProposal to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(IkeSaProposal proposal) {
+ final PersistableBundle result = SaProposalUtilsBase.toPersistableBundle(proposal);
+
+ final int[] prfArray =
+ proposal.getPseudorandomFunctions().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(PRF_KEY, prfArray);
+
+ return result;
+ }
+
+ /** Constructs an IkeSaProposal by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final IkeSaProposal.Builder builder = new IkeSaProposal.Builder();
+
+ final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+ Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+ final List<EncryptionAlgoKeyLenPair> encryptList =
+ PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+ for (EncryptionAlgoKeyLenPair t : encryptList) {
+ builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+ }
+
+ final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+ Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+ for (int algo : integrityAlgoIdArray) {
+ builder.addIntegrityAlgorithm(algo);
+ }
+
+ final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+ Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+ for (int dh : dhGroupArray) {
+ builder.addDhGroup(dh);
+ }
+
+ final int[] prfArray = in.getIntArray(PRF_KEY);
+ Objects.requireNonNull(prfArray, "PRF array was null");
+ for (int prf : prfArray) {
+ builder.addPseudorandomFunction(prf);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
new file mode 100644
index 000000000000..0c9ee8432798
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+import android.util.Pair;
+
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Abstract utility class to convert SaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+abstract class SaProposalUtilsBase {
+ static final String ENCRYPT_ALGO_KEY = "ENCRYPT_ALGO_KEY";
+ static final String INTEGRITY_ALGO_KEY = "INTEGRITY_ALGO_KEY";
+ static final String DH_GROUP_KEY = "DH_GROUP_KEY";
+
+ static class EncryptionAlgoKeyLenPair {
+ private static final String ALGO_KEY = "ALGO_KEY";
+ private static final String KEY_LEN_KEY = "KEY_LEN_KEY";
+
+ public final int encryptionAlgo;
+ public final int keyLen;
+
+ EncryptionAlgoKeyLenPair(int encryptionAlgo, int keyLen) {
+ this.encryptionAlgo = encryptionAlgo;
+ this.keyLen = keyLen;
+ }
+
+ EncryptionAlgoKeyLenPair(PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ this.encryptionAlgo = in.getInt(ALGO_KEY);
+ this.keyLen = in.getInt(KEY_LEN_KEY);
+ }
+
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(ALGO_KEY, encryptionAlgo);
+ result.putInt(KEY_LEN_KEY, keyLen);
+
+ return result;
+ }
+ }
+
+ /**
+ * Serializes common info of a SaProposal to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ static PersistableBundle toPersistableBundle(SaProposal proposal) {
+ final PersistableBundle result = new PersistableBundle();
+
+ final List<EncryptionAlgoKeyLenPair> encryptAlgoKeyLenPairs = new ArrayList<>();
+ for (Pair<Integer, Integer> pair : proposal.getEncryptionAlgorithms()) {
+ encryptAlgoKeyLenPairs.add(new EncryptionAlgoKeyLenPair(pair.first, pair.second));
+ }
+ final PersistableBundle encryptionBundle =
+ PersistableBundleUtils.fromList(
+ encryptAlgoKeyLenPairs, EncryptionAlgoKeyLenPair::toPersistableBundle);
+ result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle);
+
+ final int[] integrityAlgoIdArray =
+ proposal.getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray);
+
+ final int[] dhGroupArray = proposal.getDhGroups().stream().mapToInt(i -> i).toArray();
+ result.putIntArray(DH_GROUP_KEY, dhGroupArray);
+
+ return result;
+ }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
new file mode 100644
index 000000000000..e62acac14bd7
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Netmask;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert TunnelModeChildSessionParams to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class TunnelModeChildSessionParamsUtils {
+ private static final String TAG = TunnelModeChildSessionParamsUtils.class.getSimpleName();
+
+ private static final String INBOUND_TS_KEY = "INBOUND_TS_KEY";
+ private static final String OUTBOUND_TS_KEY = "OUTBOUND_TS_KEY";
+ private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
+ private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
+ private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
+ private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
+
+ private static class ConfigRequest {
+ private static final int TYPE_IPV4_ADDRESS = 1;
+ private static final int TYPE_IPV6_ADDRESS = 2;
+ private static final int TYPE_IPV4_DNS = 3;
+ private static final int TYPE_IPV6_DNS = 4;
+ private static final int TYPE_IPV4_DHCP = 5;
+ private static final int TYPE_IPV4_NETMASK = 6;
+
+ private static final String TYPE_KEY = "type";
+ private static final String VALUE_KEY = "address";
+ private static final String IP6_PREFIX_LEN = "ip6PrefixLen";
+
+ private static final int PREFIX_LEN_UNUSED = -1;
+
+ public final int type;
+ public final int ip6PrefixLen;
+
+ // Null when it is an empty request
+ @Nullable public final InetAddress address;
+
+ ConfigRequest(TunnelModeChildConfigRequest config) {
+ int prefixLen = PREFIX_LEN_UNUSED;
+
+ if (config instanceof ConfigRequestIpv4Address) {
+ type = TYPE_IPV4_ADDRESS;
+ address = ((ConfigRequestIpv4Address) config).getAddress();
+ } else if (config instanceof ConfigRequestIpv6Address) {
+ type = TYPE_IPV6_ADDRESS;
+ address = ((ConfigRequestIpv6Address) config).getAddress();
+ if (address != null) {
+ prefixLen = ((ConfigRequestIpv6Address) config).getPrefixLength();
+ }
+ } else if (config instanceof ConfigRequestIpv4DnsServer) {
+ type = TYPE_IPV4_DNS;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv6DnsServer) {
+ type = TYPE_IPV6_DNS;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv4DhcpServer) {
+ type = TYPE_IPV4_DHCP;
+ address = null;
+ } else if (config instanceof ConfigRequestIpv4Netmask) {
+ type = TYPE_IPV4_NETMASK;
+ address = null;
+ } else {
+ throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
+ }
+
+ ip6PrefixLen = prefixLen;
+ }
+
+ ConfigRequest(PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ type = in.getInt(TYPE_KEY);
+ ip6PrefixLen = in.getInt(IP6_PREFIX_LEN);
+
+ String addressStr = in.getString(VALUE_KEY);
+ if (addressStr == null) {
+ address = null;
+ } else {
+ address = InetAddresses.parseNumericAddress(addressStr);
+ }
+ }
+
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(TYPE_KEY, type);
+ result.putInt(IP6_PREFIX_LEN, ip6PrefixLen);
+
+ if (address != null) {
+ result.putString(VALUE_KEY, address.getHostAddress());
+ }
+
+ return result;
+ }
+ }
+
+ /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(
+ @NonNull TunnelModeChildSessionParams params) {
+ final PersistableBundle result = new PersistableBundle();
+
+ final PersistableBundle saProposalBundle =
+ PersistableBundleUtils.fromList(
+ params.getSaProposals(), ChildSaProposalUtils::toPersistableBundle);
+ result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
+
+ final PersistableBundle inTsBundle =
+ PersistableBundleUtils.fromList(
+ params.getInboundTrafficSelectors(),
+ IkeTrafficSelectorUtils::toPersistableBundle);
+ result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle);
+
+ final PersistableBundle outTsBundle =
+ PersistableBundleUtils.fromList(
+ params.getOutboundTrafficSelectors(),
+ IkeTrafficSelectorUtils::toPersistableBundle);
+ result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle);
+
+ result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
+ result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
+
+ final List<ConfigRequest> reqList = new ArrayList<>();
+ for (TunnelModeChildConfigRequest req : params.getConfigurationRequests()) {
+ reqList.add(new ConfigRequest(req));
+ }
+ final PersistableBundle configReqListBundle =
+ PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
+ result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
+
+ return result;
+ }
+
+ private static List<IkeTrafficSelector> getTsFromPersistableBundle(
+ PersistableBundle in, String key) {
+ PersistableBundle tsBundle = in.getPersistableBundle(key);
+ Objects.requireNonNull(tsBundle, "Value for key " + key + " was null");
+ return PersistableBundleUtils.toList(
+ tsBundle, IkeTrafficSelectorUtils::fromPersistableBundle);
+ }
+
+ /** Constructs a TunnelModeChildSessionParams by deserializing a PersistableBundle. */
+ @NonNull
+ public static TunnelModeChildSessionParams fromPersistableBundle(
+ @NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final TunnelModeChildSessionParams.Builder builder =
+ new TunnelModeChildSessionParams.Builder();
+
+ final PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
+ Objects.requireNonNull(proposalBundle, "SA proposal was null");
+ final List<ChildSaProposal> proposals =
+ PersistableBundleUtils.toList(
+ proposalBundle, ChildSaProposalUtils::fromPersistableBundle);
+ for (ChildSaProposal p : proposals) {
+ builder.addSaProposal(p);
+ }
+
+ for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, INBOUND_TS_KEY)) {
+ builder.addInboundTrafficSelectors(ts);
+ }
+
+ for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, OUTBOUND_TS_KEY)) {
+ builder.addOutboundTrafficSelectors(ts);
+ }
+
+ builder.setLifetimeSeconds(
+ in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
+ final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
+ Objects.requireNonNull(configReqListBundle, "Config request list was null");
+ final List<ConfigRequest> reqList =
+ PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
+
+ boolean hasIpv4AddressReq = false;
+ boolean hasIpv4NetmaskReq = false;
+ for (ConfigRequest req : reqList) {
+ switch (req.type) {
+ case ConfigRequest.TYPE_IPV4_ADDRESS:
+ hasIpv4AddressReq = true;
+ if (req.address == null) {
+ builder.addInternalAddressRequest(AF_INET);
+ } else {
+ builder.addInternalAddressRequest((Inet4Address) req.address);
+ }
+ break;
+ case ConfigRequest.TYPE_IPV6_ADDRESS:
+ if (req.address == null) {
+ builder.addInternalAddressRequest(AF_INET6);
+ } else {
+ builder.addInternalAddressRequest(
+ (Inet6Address) req.address, req.ip6PrefixLen);
+ }
+ break;
+ case ConfigRequest.TYPE_IPV4_NETMASK:
+ // Do not need to set netmask because it will be automatically set by the
+ // builder when an IPv4 internal address request is set.
+ hasIpv4NetmaskReq = true;
+ break;
+ case ConfigRequest.TYPE_IPV4_DNS:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv4 DNS server is unsupported");
+ }
+ builder.addInternalDnsServerRequest(AF_INET);
+ break;
+ case ConfigRequest.TYPE_IPV6_DNS:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv6 DNS server is unsupported");
+ }
+ builder.addInternalDnsServerRequest(AF_INET6);
+ break;
+ case ConfigRequest.TYPE_IPV4_DHCP:
+ if (req.address != null) {
+ Log.w(TAG, "Requesting a specific IPv4 DHCP server is unsupported");
+ }
+ builder.addInternalDhcpServerRequest(AF_INET);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unrecognized config request type: " + req.type);
+ }
+ }
+
+ if (hasIpv4AddressReq != hasIpv4NetmaskReq) {
+ Log.w(
+ TAG,
+ String.format(
+ "Expect IPv4 address request and IPv4 netmask request either both"
+ + " exist or both absent, but found hasIpv4AddressReq exists? %b,"
+ + " hasIpv4AddressReq exists? %b, ",
+ hasIpv4AddressReq, hasIpv4NetmaskReq));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 74df1b2b9194..a5b0e8d149ef 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1288,10 +1288,12 @@ public class Build {
public static final String HOST = getString("ro.build.host");
/**
- * Returns true if we are running a debug build such as "user-debug" or "eng".
- * @hide
+ * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+ *
+ * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+ * application regardless of whether they have the "debuggable" attribute set, or downgrade
+ * selinux into "permissive" mode in particular.
*/
- @UnsupportedAppUsage
public static final boolean IS_DEBUGGABLE =
SystemProperties.getInt("ro.debuggable", 0) == 1;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index c8e682c86ea7..e068772e954a 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -76,7 +76,9 @@ public abstract class CombinedVibrationEffect implements Parcelable {
* A sequential vibration effect should be performed by multiple vibrators in order.
*
* @see CombinedVibrationEffect.SequentialCombination
+ * @hide
*/
+ @TestApi
@NonNull
public static SequentialCombination startSequential() {
return new SequentialCombination();
@@ -162,7 +164,9 @@ public abstract class CombinedVibrationEffect implements Parcelable {
* A combination of haptic effects that should be played in multiple vibrators in sequence.
*
* @see CombinedVibrationEffect#startSequential()
+ * @hide
*/
+ @TestApi
public static final class SequentialCombination {
private final ArrayList<CombinedVibrationEffect> mEffects = new ArrayList<>();
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 0587610630a6..03e5f1d59b86 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -503,6 +503,20 @@ public abstract class VibrationEffect implements Parcelable {
}
/** @hide */
+ public static String effectStrengthToString(int effectStrength) {
+ switch (effectStrength) {
+ case EFFECT_STRENGTH_LIGHT:
+ return "LIGHT";
+ case EFFECT_STRENGTH_MEDIUM:
+ return "MEDIUM";
+ case EFFECT_STRENGTH_STRONG:
+ return "STRONG";
+ default:
+ return Integer.toString(effectStrength);
+ }
+ }
+
+ /** @hide */
@TestApi
public static class OneShot extends VibrationEffect implements Parcelable {
private final long mDuration;
@@ -936,8 +950,8 @@ public abstract class VibrationEffect implements Parcelable {
@Override
public String toString() {
- return "Prebaked{mEffectId=" + mEffectId
- + ", mEffectStrength=" + mEffectStrength
+ return "Prebaked{mEffectId=" + effectIdToString(mEffectId)
+ + ", mEffectStrength=" + effectStrengthToString(mEffectStrength)
+ ", mFallback=" + mFallback
+ ", mFallbackEffect=" + mFallbackEffect
+ "}";
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 07272e756e77..50d2de3da965 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -18,6 +18,7 @@ package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.vibrator.IVibrator;
import android.util.SparseBooleanArray;
import java.util.ArrayList;
@@ -33,20 +34,7 @@ import java.util.Objects;
* @hide
*/
public final class VibratorInfo implements Parcelable {
-
- /**
- * Capability to set amplitude values to vibrations.
- * @hide
- */
- // Internally this maps to the HAL constant IVibrator::CAP_AMPLITUDE_CONTROL
- public static final int CAPABILITY_AMPLITUDE_CONTROL = 4;
-
- /**
- * Capability to compose primitives into a single effect.
- * @hide
- */
- // Internally this maps to the HAL constant IVibrator::CAP_COMPOSE_EFFECTS
- public static final int CAPABILITY_COMPOSE_EFFECTS = 32;
+ private static final String TAG = "VibratorInfo";
private final int mId;
private final long mCapabilities;
@@ -108,7 +96,7 @@ public final class VibratorInfo implements Parcelable {
return "VibratorInfo{"
+ "mId=" + mId
+ ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
- + ", mCapabilities flags=" + mCapabilities
+ + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ '}';
@@ -125,7 +113,7 @@ public final class VibratorInfo implements Parcelable {
* @return True if the hardware can control the amplitude of the vibrations, otherwise false.
*/
public boolean hasAmplitudeControl() {
- return hasCapability(CAPABILITY_AMPLITUDE_CONTROL);
+ return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
}
/**
@@ -153,7 +141,7 @@ public final class VibratorInfo implements Parcelable {
* @return Whether the primitive is supported.
*/
public boolean isPrimitiveSupported(@VibrationEffect.Composition.Primitive int primitiveId) {
- return hasCapability(CAPABILITY_COMPOSE_EFFECTS) && mSupportedPrimitives != null
+ return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
&& mSupportedPrimitives.get(primitiveId, false);
}
@@ -170,12 +158,27 @@ public final class VibratorInfo implements Parcelable {
private String[] getCapabilitiesNames() {
List<String> names = new ArrayList<>();
- if (hasCapability(CAPABILITY_AMPLITUDE_CONTROL)) {
- names.add("AMPLITUDE_CONTROL");
+ if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
+ names.add("ON_CALLBACK");
+ }
+ if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
+ names.add("PERFORM_CALLBACK");
}
- if (hasCapability(CAPABILITY_COMPOSE_EFFECTS)) {
+ if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
names.add("COMPOSE_EFFECTS");
}
+ if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+ names.add("ALWAYS_ON_CONTROL");
+ }
+ if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
+ names.add("AMPLITUDE_CONTROL");
+ }
+ if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ names.add("EXTERNAL_CONTROL");
+ }
+ if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
+ names.add("EXTERNAL_AMPLITUDE_CONTROL");
+ }
return names.toArray(new String[names.size()]);
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 71a849963818..c967deb5e810 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2766,10 +2766,11 @@ public class StorageManager {
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void notifyAppIoBlocked(@NonNull String volumeUuid, int uid, int tid,
+ public void notifyAppIoBlocked(@NonNull UUID volumeUuid, int uid, int tid,
@AppIoBlockedReason int reason) {
+ Objects.requireNonNull(volumeUuid);
try {
- mStorageManager.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
+ mStorageManager.notifyAppIoBlocked(convert(volumeUuid), uid, tid, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2792,10 +2793,11 @@ public class StorageManager {
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void notifyAppIoResumed(@NonNull String volumeUuid, int uid, int tid,
+ public void notifyAppIoResumed(@NonNull UUID volumeUuid, int uid, int tid,
@AppIoBlockedReason int reason) {
+ Objects.requireNonNull(volumeUuid);
try {
- mStorageManager.notifyAppIoResumed(volumeUuid, uid, tid, reason);
+ mStorageManager.notifyAppIoResumed(convert(volumeUuid), uid, tid, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index b12bb2ece4c2..396ba2d3cea5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IVold;
+import java.util.List;
import java.util.Set;
/**
@@ -112,4 +113,10 @@ public abstract class StorageManagerInternal {
* @param bytes number of bytes which need to be freed
*/
public abstract void freeCache(@Nullable String volumeUuid, long bytes);
+
+ /**
+ * Returns the {@link VolumeInfo#getId()} values for the volumes matching
+ * {@link VolumeInfo#isPrimary()}
+ */
+ public abstract List<String> getPrimaryVolumeIds();
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 084b18eb2999..913b827332bf 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -668,7 +668,7 @@ public final class PermissionControllerManager {
public void getPrivilegesDescriptionStringForProfile(
@NonNull String profileName,
@NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<String> callback) {
+ @NonNull Consumer<CharSequence> callback) {
mRemoteService.postAsync(service -> {
AndroidFuture<String> future = new AndroidFuture<>();
service.getPrivilegesDescriptionStringForProfile(profileName, future);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6e89faf9c2ed..e9bbcc79e9df 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -505,6 +505,22 @@ public final class DeviceConfig {
"connectivity_thermal_power_manager";
/**
+ * Namespace for all statsd java features that can be applied immediately.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
+
+ /**
+ * Namespace for all statsd java features that are applied on boot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
+
+ /**
* Namespace for all statsd native features that can be applied immediately.
*
* @hide
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 1e07a8748af9..bbe184bd1a8c 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -239,14 +239,13 @@ public abstract class ExternalStorageService extends Service {
}
@Override
- public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason,
- RemoteCallback callback) throws RemoteException {
+ public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
+ throws RemoteException {
mHandler.post(() -> {
try {
onAnrDelayStarted(packageName, uid, tid, reason);
- sendResult(packageName, null /* throwable */, callback);
} catch (Throwable t) {
- sendResult(packageName, t, callback);
+ // Ignored
}
});
}
diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl
index ba98efa58f7c..0766b754e57d 100644
--- a/core/java/android/service/storage/IExternalStorageService.aidl
+++ b/core/java/android/service/storage/IExternalStorageService.aidl
@@ -32,6 +32,5 @@ oneway interface IExternalStorageService
in RemoteCallback callback);
void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes,
in RemoteCallback callback);
- void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason,
- in RemoteCallback callback);
+ void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason);
} \ No newline at end of file
diff --git a/core/java/android/view/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java
new file mode 100644
index 000000000000..c159a127f4eb
--- /dev/null
+++ b/core/java/android/view/InputEventAssigner.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+/**
+ * Process input events and assign input event id to a specific frame.
+ *
+ * The assigned input event id is determined by where the current gesture is relative to the vsync.
+ * In the middle of the gesture (we already processed some input events, and already received at
+ * least 1 vsync), the latest InputEvent is assigned to the next frame.
+ * If a gesture just started, then the ACTION_DOWN event will be assigned to the next frame.
+ *
+ * Consider the following sequence:
+ * DOWN -> VSYNC 1 -> MOVE 1 -> MOVE 2 -> VSYNC 2.
+ *
+ * For VSYNC 1, we will assign the "DOWN" input event.
+ * For VSYNC 2, we will assign the "MOVE 2" input event.
+ *
+ * Consider another sequence:
+ * DOWN -> MOVE 1 -> MOVE 2 -> VSYNC 1 -> MOVE 3 -> VSYNC 2.
+ *
+ * For VSYNC 1, we will still assign the "DOWN" input event. That means that "MOVE 1" and "MOVE 2"
+ * events are not attributed to any frame.
+ * For VSYNC 2, the "MOVE 3" input event will be assigned.
+ *
+ * @hide
+ */
+public class InputEventAssigner {
+ private static final String TAG = "InputEventAssigner";
+ private boolean mHasUnprocessedDown = false;
+ private int mEventId = INVALID_INPUT_EVENT_ID;
+
+ /**
+ * Notify InputEventAssigner that the Choreographer callback has been processed. This will reset
+ * the 'down' state to assign the latest input event to the current frame.
+ */
+ public void onChoreographerCallback() {
+ // Mark completion of this frame. Use newest input event from now on.
+ mHasUnprocessedDown = false;
+ }
+
+ /**
+ * Process the provided input event to determine which event id to assign to the current frame.
+ * @param event the input event currently being processed
+ * @return the id of the input event to use for the current frame
+ */
+ public int processEvent(InputEvent event) {
+ if (event instanceof KeyEvent) {
+ // We will not do any special handling for key events
+ return event.getId();
+ }
+
+ if (event instanceof MotionEvent) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ final int action = motionEvent.getActionMasked();
+
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mHasUnprocessedDown = false;
+ }
+ if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN) && action == MotionEvent.ACTION_DOWN) {
+ mHasUnprocessedDown = true;
+ mEventId = event.getId();
+ // This will remain 'true' even if we receive a MOVE event, as long as choreographer
+ // hasn't invoked the 'CALLBACK_INPUT' callback.
+ }
+ // Don't update the event id if we haven't processed DOWN yet.
+ if (!mHasUnprocessedDown) {
+ mEventId = event.getId();
+ }
+ return mEventId;
+ }
+
+ throw new IllegalArgumentException("Received unexpected " + event);
+ }
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index d67439cc9de2..6801c27851a9 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3589,6 +3589,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
msg.append(", deviceId=").append(getDeviceId());
msg.append(", source=0x").append(Integer.toHexString(getSource()));
msg.append(", displayId=").append(getDisplayId());
+ msg.append(", eventId=").append(getId());
}
msg.append(" }");
return msg.toString();
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 5ce4c50cc85c..6c8753b95cc1 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -27,7 +27,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
-import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -42,7 +42,7 @@ import java.util.ArrayList;
* @hide
*/
@RemoteViews.RemoteView
-public class NotificationHeaderView extends FrameLayout {
+public class NotificationHeaderView extends RelativeLayout {
private final int mHeadingEndMargin;
private final int mTouchableHeight;
private OnClickListener mExpandClickListener;
@@ -159,7 +159,7 @@ public class NotificationHeaderView extends FrameLayout {
* @param extraMarginEnd extra margin in px
*/
public void setTopLineExtraMarginEnd(int extraMarginEnd) {
- mTopLineView.setHeaderTextMarginEnd(extraMarginEnd + mHeadingEndMargin);
+ mTopLineView.setHeaderTextMarginEnd(extraMarginEnd);
}
/**
@@ -181,12 +181,15 @@ public class NotificationHeaderView extends FrameLayout {
* @return extra margin
*/
public int getTopLineExtraMarginEnd() {
- return mTopLineView.getHeaderTextMarginEnd() - mHeadingEndMargin;
+ return mTopLineView.getHeaderTextMarginEnd();
}
/**
* Get the base margin at the end of the top line view.
* Add this to {@link #getTopLineExtraMarginEnd()} to get the total margin of the top line.
+ * <p>
+ * NOTE: This method's result is only valid if the expander does not have a number. Currently
+ * only groups headers and conversations have numbers, so this is safe to use by MediaStyle.
*
* @return base margin
*/
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index c2f17c310363..ec23a29b2070 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -310,6 +310,18 @@ public class ViewConfiguration {
*/
private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f;
+ /**
+ * The timeout value in milliseconds to adjust the selection span and actions for the selected
+ * text when TextClassifier has been initialized.
+ */
+ private static final int SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND = 200;
+
+ /**
+ * The timeout value in milliseconds to adjust the selection span and actions for the selected
+ * text when TextClassifier has not been initialized.
+ */
+ private static final int SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND = 500;
+
private final boolean mConstructedWithContext;
private final int mEdgeSlop;
private final int mFadingEdgeLength;
@@ -335,6 +347,8 @@ public class ViewConfiguration {
private final float mHorizontalScrollFactor;
private final boolean mShowMenuShortcutsWhenKeyboardPresent;
private final long mScreenshotChordKeyTimeout;
+ private final int mSmartSelectionInitializedTimeout;
+ private final int mSmartSelectionInitializingTimeout;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915)
private boolean sHasPermanentMenuKey;
@@ -378,6 +392,8 @@ public class ViewConfiguration {
// Getter throws if mConstructedWithContext is false so doesn't matter what
// this value is.
mMinScalingSpan = 0;
+ mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
+ mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
}
/**
@@ -488,6 +504,11 @@ public class ViewConfiguration {
mScreenshotChordKeyTimeout = res.getInteger(
com.android.internal.R.integer.config_screenshotChordKeyTimeout);
+
+ mSmartSelectionInitializedTimeout = res.getInteger(
+ com.android.internal.R.integer.config_smartSelectionInitializedTimeoutMillis);
+ mSmartSelectionInitializingTimeout = res.getInteger(
+ com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis);
}
/**
@@ -1069,6 +1090,24 @@ public class ViewConfiguration {
}
/**
+ * @return the timeout value in milliseconds to adjust the selection span and actions for the
+ * selected text when TextClassifier has been initialized.
+ * @hide
+ */
+ public int getSmartSelectionInitializedTimeout() {
+ return mSmartSelectionInitializedTimeout;
+ }
+
+ /**
+ * @return the timeout value in milliseconds to adjust the selection span and actions for the
+ * selected text when TextClassifier has not been initialized.
+ * @hide
+ */
+ public int getSmartSelectionInitializingTimeout() {
+ return mSmartSelectionInitializingTimeout;
+ }
+
+ /**
* @return the duration in milliseconds before an end of a long press causes a tooltip to be
* hidden
* @hide
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
index d4aaa611f800..36bf53201e6f 100644
--- a/core/java/android/view/ViewFrameInfo.java
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.FrameInfo;
+import android.os.IInputConstants;
/**
* The timing information of events taking place in ViewRootImpl
@@ -24,32 +25,14 @@ import android.graphics.FrameInfo;
*/
public class ViewFrameInfo {
public long drawStart;
- public long oldestInputEventTime; // the time of the oldest input event consumed for this frame
- public long newestInputEventTime; // the time of the newest input event consumed for this frame
+
+
// Various flags set to provide extra metadata about the current frame. See flag definitions
// inside FrameInfo.
// @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
public long flags;
- /**
- * Update the oldest event time.
- * @param eventTime the time of the input event
- */
- public void updateOldestInputEvent(long eventTime) {
- if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) {
- oldestInputEventTime = eventTime;
- }
- }
-
- /**
- * Update the newest event time.
- * @param eventTime the time of the input event
- */
- public void updateNewestInputEvent(long eventTime) {
- if (newestInputEventTime == 0 || eventTime > newestInputEventTime) {
- newestInputEventTime = eventTime;
- }
- }
+ private int mInputEventId;
/**
* Populate the missing fields using the data from ViewFrameInfo
@@ -58,8 +41,7 @@ public class ViewFrameInfo {
public void populateFrameInfo(FrameInfo frameInfo) {
frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
- // TODO(b/169866723): Use InputEventAssigner
- frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime;
+ frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = mInputEventId;
}
/**
@@ -67,8 +49,7 @@ public class ViewFrameInfo {
*/
public void reset() {
drawStart = 0;
- oldestInputEventTime = 0;
- newestInputEventTime = 0;
+ mInputEventId = IInputConstants.INVALID_INPUT_EVENT_ID;
flags = 0;
}
@@ -78,4 +59,12 @@ public class ViewFrameInfo {
public void markDrawStart() {
drawStart = System.nanoTime();
}
+
+ /**
+ * Assign the value for input event id
+ * @param eventId the id of the input event
+ */
+ public void setInputEvent(int eventId) {
+ mInputEventId = eventId;
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f8e65bd0d056..390e3ae78143 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -457,6 +457,7 @@ public final class ViewRootImpl implements ViewParent,
FallbackEventHandler mFallbackEventHandler;
final Choreographer mChoreographer;
protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
+ private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
@@ -8352,16 +8353,7 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
- long eventTime = q.mEvent.getEventTimeNano();
- long oldestEventTime = eventTime;
- if (q.mEvent instanceof MotionEvent) {
- MotionEvent me = (MotionEvent)q.mEvent;
- if (me.getHistorySize() > 0) {
- oldestEventTime = me.getHistoricalEventTimeNano(0);
- }
- }
- mViewFrameInfo.updateOldestInputEvent(oldestEventTime);
- mViewFrameInfo.updateNewestInputEvent(eventTime);
+ mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
deliverInputEvent(q);
}
@@ -8497,6 +8489,11 @@ public final class ViewRootImpl implements ViewParent,
consumedBatches = false;
}
doProcessInputEvents();
+ if (consumedBatches) {
+ // Must be done after we processed the input events, to mark the completion of the frame
+ // from the input point of view
+ mInputEventAssigner.onChoreographerCallback();
+ }
return consumedBatches;
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index b1b443f919d9..a64111069c9b 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -22,6 +22,7 @@ import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IDataShareWriteAdapter;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -101,4 +102,10 @@ oneway interface IContentCaptureManager {
* Sets whether the default service should be used.
*/
void setDefaultServiceEnabled(int userId, boolean enabled);
+
+ /**
+ * Registers a listener to handle updates ContentCaptureOptions from server.
+ */
+ void registerContentCaptureOptionsCallback(String packageName,
+ in IContentCaptureOptionsCallback callback);
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl b/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl
new file mode 100644
index 000000000000..b0f062de3bb9
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.ContentCaptureOptions;
+
+/**
+ * Callback for changes to content capture options made by ContentCaptureService.
+ * Callback interface used by IContentCaptureManager to send asynchronous
+ * notifications back to its clients. Note that this is a
+ * one-way interface so the server does not block waiting for the client.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureOptionsCallback {
+ void setContentCaptureOptions(in ContentCaptureOptions options);
+}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 15b29adafddd..04d29ee3d48a 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -66,12 +66,19 @@ public interface DisplayHashResultCallback {
*/
int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4;
+ /**
+ * The hash algorithm sent to generate the hash was invalid. This means the value is not one
+ * of the supported values in {@link DisplayHashManager#getSupportedHashAlgorithms()}
+ */
+ int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
+
/** @hide */
@IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
DISPLAY_HASH_ERROR_UNKNOWN,
DISPLAY_HASH_ERROR_INVALID_BOUNDS,
DISPLAY_HASH_ERROR_MISSING_WINDOW,
- DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
+ DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
})
@Retention(RetentionPolicy.SOURCE)
@interface DisplayHashErrorCode {
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 2975afc084e6..d0959f97e438 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -90,6 +90,12 @@ public final class TextClassificationConstants {
static final String SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND =
"system_textclassifier_api_timeout_in_second";
+ /**
+ * The max amount of characters before and after the selected text that are passed to the
+ * TextClassifier for the smart selection.
+ */
+ private static final String SMART_SELECTION_TRIM_DELTA = "smart_selection_trim_delta";
+
private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -100,6 +106,7 @@ public final class TextClassificationConstants {
private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
private static final long SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT = 60;
+ private static final int SMART_SELECTION_TRIM_DELTA_DEFAULT = 120;
@Nullable
public String getTextClassifierServicePackageOverride() {
@@ -155,6 +162,12 @@ public final class TextClassificationConstants {
SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT);
}
+ public int getSmartSelectionTrimDelta() {
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SMART_SELECTION_TRIM_DELTA,
+ SMART_SELECTION_TRIM_DELTA_DEFAULT);
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
@@ -170,6 +183,7 @@ public final class TextClassificationConstants {
getTextClassifierServicePackageOverride()).println();
pw.print(SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
getSystemTextClassifierApiTimeoutInSecond()).println();
+ pw.print(SMART_SELECTION_TRIM_DELTA, getSmartSelectionTrimDelta()).println();
pw.decreaseIndent();
}
} \ No newline at end of file
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6f189204434a..eb6bce4a2f59 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -36,6 +36,7 @@ import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.Log;
import android.view.ActionMode;
+import android.view.ViewConfiguration;
import android.view.textclassifier.ExtrasUtils;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.SelectionEvent.InvocationMethod;
@@ -1056,10 +1057,12 @@ public final class SelectionActionModeHelper {
*/
private static final class TextClassificationHelper {
- private static final int TRIM_DELTA = 120; // characters
+ // The fixed upper bound of context size.
+ private static final int TRIM_DELTA_UPPER_BOUND = 240;
private final Context mContext;
private Supplier<TextClassifier> mTextClassifier;
+ private final ViewConfiguration mViewConfiguration;
/** The original TextView text. **/
private String mText;
@@ -1088,12 +1091,13 @@ public final class SelectionActionModeHelper {
private SelectionResult mLastClassificationResult;
/** Whether the TextClassifier has been initialized. */
- private boolean mHot;
+ private boolean mInitialized;
TextClassificationHelper(Context context, Supplier<TextClassifier> textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
init(textClassifier, text, selectionStart, selectionEnd, locales);
mContext = Objects.requireNonNull(context);
+ mViewConfiguration = ViewConfiguration.get(mContext);
}
@UiThread
@@ -1110,13 +1114,13 @@ public final class SelectionActionModeHelper {
@WorkerThread
public SelectionResult classifyText() {
- mHot = true;
+ mInitialized = true;
return performClassification(null /* selection */);
}
@WorkerThread
public SelectionResult suggestSelection() {
- mHot = true;
+ mInitialized = true;
trimText();
final TextSelection selection;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
@@ -1148,16 +1152,15 @@ public final class SelectionActionModeHelper {
/**
* Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
*/
- // TODO: Consider making this a ViewConfiguration.
public int getTimeoutDuration() {
- if (mHot) {
- return 200;
+ if (mInitialized) {
+ return mViewConfiguration.getSmartSelectionInitializedTimeout();
} else {
// Return a slightly larger number than usual when the TextClassifier is first
// initialized. Initialization would usually take longer than subsequent calls to
// the TextClassifier. The impact of this on the UI is that we do not show the
// selection handles or toolbar until after this timeout.
- return 500;
+ return mViewConfiguration.getSmartSelectionInitializingTimeout();
}
}
@@ -1205,8 +1208,11 @@ public final class SelectionActionModeHelper {
}
private void trimText() {
- mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
- final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
+ final int trimDelta = Math.min(
+ TextClassificationManager.getSettings(mContext).getSmartSelectionTrimDelta(),
+ TRIM_DELTA_UPPER_BOUND);
+ mTrimStart = Math.max(0, mSelectionStart - trimDelta);
+ final int referenceEnd = Math.min(mText.length(), mSelectionEnd + trimDelta);
mTrimmedText = mText.subSequence(mTrimStart, referenceEnd);
mRelativeStart = mSelectionStart - mTrimStart;
mRelativeEnd = mSelectionEnd - mTrimStart;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 3a9f3b9c1128..eecd0cfe66a4 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -74,11 +74,11 @@ interface IAppOpsService {
@UnsupportedAppUsage
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
void getHistoricalOps(int uid, String packageName, String attributionTag, in List<String> ops,
- int filter, long beginTimeMillis, long endTimeMillis, int flags,
+ int historyFlags, int filter, long beginTimeMillis, long endTimeMillis, int flags,
in RemoteCallback callback);
void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
- in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags,
- in RemoteCallback callback);
+ in List<String> ops, int historyFlags, int filter, long beginTimeMillis,
+ long endTimeMillis, int flags, in RemoteCallback callback);
void offsetHistory(long duration);
void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java
index 3c081e27305c..7529536de66b 100644
--- a/core/java/com/android/internal/infra/GlobalWhitelistState.java
+++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java
@@ -99,6 +99,18 @@ public class GlobalWhitelistState {
}
/**
+ * Gets packages that are either entirely allowlisted or have components that are allowlisted
+ * for the given user.
+ */
+ public ArraySet<String> getWhitelistedPackages(@UserIdInt int userId) {
+ synchronized (mGlobalWhitelistStateLock) {
+ if (mWhitelisterHelpers == null) return null;
+ final WhitelistHelper helper = mWhitelisterHelpers.get(userId);
+ return helper == null ? null : helper.getWhitelistedPackages();
+ }
+ }
+
+ /**
* Resets the allowlist for the given user.
*/
public void resetWhitelist(@NonNull int userId) {
diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java
index 1d76090f59f3..3e93106822a2 100644
--- a/core/java/com/android/internal/infra/WhitelistHelper.java
+++ b/core/java/com/android/internal/infra/WhitelistHelper.java
@@ -140,6 +140,15 @@ public final class WhitelistHelper {
return mWhitelistedPackages == null ? null : mWhitelistedPackages.get(packageName);
}
+ /**
+ * Returns a set of all packages that are either entirely allowlisted or have components that
+ * are allowlisted.
+ */
+ @Nullable
+ public ArraySet<String> getWhitelistedPackages() {
+ return mWhitelistedPackages == null ? null : new ArraySet<>(mWhitelistedPackages.keySet());
+ }
+
@Override
public String toString() {
return "WhitelistHelper[" + mWhitelistedPackages + ']';
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 342456a58091..33ee8f04d83e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -203,6 +203,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
mCancelled = true;
removeObservers();
+ if (mListener != null) {
+ mListener.onNotifyCujEvents(mSession, InteractionJankMonitor.ACTION_SESSION_CANCEL);
+ }
}
@Override
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 0294ec398484..fbc92c1f99c4 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -99,7 +99,7 @@ public class InteractionJankMonitor {
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
- public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
+ public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 1b1e0bfb3a58..8ecc80946141 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -32,7 +32,6 @@ import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
@@ -62,11 +61,9 @@ import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.graphics.ColorUtils;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
import java.util.function.Consumer;
@@ -118,7 +115,6 @@ public class ConversationLayout extends FrameLayout
private ViewGroup mExpandButtonAndContentContainer;
private NotificationExpandButton mExpandButton;
private MessagingLinearLayout mImageMessageContainer;
- private int mExpandButtonExpandedTopMargin;
private int mBadgedSideMargins;
private int mConversationAvatarSize;
private int mConversationAvatarSizeExpanded;
@@ -147,7 +143,6 @@ public class ConversationLayout extends FrameLayout
private int mFacePileProtectionWidth;
private int mFacePileProtectionWidthExpanded;
private boolean mImportantConversation;
- private TextView mUnreadBadge;
private View mFeedbackIcon;
private float mMinTouchSize;
private Icon mConversationIcon;
@@ -245,8 +240,6 @@ public class ConversationLayout extends FrameLayout
mContentContainer = findViewById(R.id.notification_action_list_margin_target);
mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
mExpandButton = findViewById(R.id.expand_button);
- mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
- R.dimen.conversation_expand_button_top_margin_expanded);
mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize(
R.dimen.conversation_header_expanded_padding_end);
mContentMarginEnd = getResources().getDimensionPixelSize(
@@ -286,7 +279,6 @@ public class ConversationLayout extends FrameLayout
mAppName.setOnVisibilityChangedListener((visibility) -> {
onAppNameVisibilityChanged();
});
- mUnreadBadge = findViewById(R.id.conversation_unread_count);
mConversationContentStart = getResources().getDimensionPixelSize(
R.dimen.conversation_content_start);
mInternalButtonPadding
@@ -426,17 +418,7 @@ public class ConversationLayout extends FrameLayout
/** @hide */
public void setUnreadCount(int unreadCount) {
- boolean visible = mIsCollapsed && unreadCount > 1;
- mUnreadBadge.setVisibility(visible ? VISIBLE : GONE);
- if (visible) {
- CharSequence text = unreadCount >= 100
- ? getResources().getString(R.string.unread_convo_overflow, 99)
- : String.format(Locale.getDefault(), "%d", unreadCount);
- mUnreadBadge.setText(text);
- mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor));
- boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f;
- mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE);
- }
+ mExpandButton.setNumber(unreadCount);
}
private void addRemoteInputHistoryToMessages(
@@ -1132,15 +1114,16 @@ public class ConversationLayout extends FrameLayout
}
private void updateExpandButton() {
- int gravity;
- int topMargin = 0;
+ int buttonGravity;
+ int containerHeight;
ViewGroup newContainer;
if (mIsCollapsed) {
- gravity = Gravity.CENTER;
+ buttonGravity = Gravity.CENTER;
+ containerHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
newContainer = mExpandButtonAndContentContainer;
} else {
- gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- topMargin = mExpandButtonExpandedTopMargin;
+ buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ containerHeight = ViewGroup.LayoutParams.MATCH_PARENT;
newContainer = this;
}
mExpandButton.setExpanded(!mIsCollapsed);
@@ -1149,14 +1132,14 @@ public class ConversationLayout extends FrameLayout
// content when collapsed, but allows the content to flow under it when expanded.
if (newContainer != mExpandButtonContainer.getParent()) {
((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer);
+ mExpandButtonContainer.getLayoutParams().height = containerHeight;
newContainer.addView(mExpandButtonContainer);
}
// update if the expand button is centered
LinearLayout.LayoutParams layoutParams =
(LinearLayout.LayoutParams) mExpandButton.getLayoutParams();
- layoutParams.gravity = gravity;
- layoutParams.topMargin = topMargin;
+ layoutParams.gravity = buttonGravity;
mExpandButton.setLayoutParams(layoutParams);
}
@@ -1210,6 +1193,7 @@ public class ConversationLayout extends FrameLayout
mExpandButtonContainer.setVisibility(GONE);
mConversationIconContainer.setOnClickListener(null);
}
+ mExpandButton.setVisibility(VISIBLE);
updateContentEndPaddings();
}
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index 8add34f328bf..fc4cc5764eaf 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -16,30 +16,42 @@
package com.android.internal.widget;
-import static com.android.internal.widget.ColoredIconHelper.applyGrayTint;
-
+import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
+import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
+import java.util.Locale;
+
/**
* An expand button in a notification
*/
@RemoteViews.RemoteView
-public class NotificationExpandButton extends ImageView {
+public class NotificationExpandButton extends FrameLayout {
- private final int mMinTouchTargetSize;
+ private View mPillView;
+ private TextView mNumberView;
+ private ImageView mIconView;
private boolean mExpanded;
- private int mOriginalNotificationColor;
+ private int mNumber;
+ private int mDefaultPillColor;
+ private int mDefaultTextColor;
+ private int mHighlightPillColor;
+ private int mHighlightTextColor;
+ private boolean mDisallowColor;
public NotificationExpandButton(Context context) {
this(context, null, 0, 0);
@@ -57,7 +69,14 @@ public class NotificationExpandButton extends ImageView {
public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mMinTouchTargetSize = (int) (getResources().getDisplayMetrics().density * 48 + 0.5);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPillView = findViewById(R.id.expand_button_pill);
+ mNumberView = findViewById(R.id.expand_button_number);
+ mIconView = findViewById(R.id.expand_button_icon);
}
/**
@@ -72,7 +91,6 @@ public class NotificationExpandButton extends ImageView {
} else {
super.getBoundsOnScreen(outRect, clipToParent);
}
- extendRectToMinTouchSize(outRect);
}
/**
@@ -89,32 +107,12 @@ public class NotificationExpandButton extends ImageView {
return super.pointInView(localX, localY, slop);
}
- @RemotableViewMethod
- public void setOriginalNotificationColor(int color) {
- mOriginalNotificationColor = color;
- }
-
- public int getOriginalNotificationColor() {
- return mOriginalNotificationColor;
- }
-
/**
- * Set the button's color filter: to gray if true, otherwise colored.
- * If this button has no original color, this has no effect.
+ * Disable the use of the accent colors for this view, if true.
*/
public void setGrayedOut(boolean shouldApply) {
- applyGrayTint(mContext, getDrawable(), shouldApply, mOriginalNotificationColor);
- }
-
- private void extendRectToMinTouchSize(Rect rect) {
- if (rect.width() < mMinTouchTargetSize) {
- rect.left = rect.centerX() - mMinTouchTargetSize / 2;
- rect.right = rect.left + mMinTouchTargetSize;
- }
- if (rect.height() < mMinTouchTargetSize) {
- rect.top = rect.centerY() - mMinTouchTargetSize / 2;
- rect.bottom = rect.top + mMinTouchTargetSize;
- }
+ mDisallowColor = shouldApply;
+ updateColors();
}
@Override
@@ -129,10 +127,10 @@ public class NotificationExpandButton extends ImageView {
@RemotableViewMethod
public void setExpanded(boolean expanded) {
mExpanded = expanded;
- updateExpandButton();
+ updateExpandedState();
}
- private void updateExpandButton() {
+ private void updateExpandedState() {
int drawableId;
int contentDescriptionId;
if (mExpanded) {
@@ -142,8 +140,89 @@ public class NotificationExpandButton extends ImageView {
drawableId = R.drawable.ic_expand_notification;
contentDescriptionId = R.string.expand_button_content_description_collapsed;
}
- setImageDrawable(getContext().getDrawable(drawableId));
- setColorFilter(mOriginalNotificationColor);
setContentDescription(mContext.getText(contentDescriptionId));
+ mIconView.setImageDrawable(getContext().getDrawable(drawableId));
+
+ // changing the expanded state can affect the number display
+ updateNumber();
+ }
+
+ private void updateNumber() {
+ if (shouldShowNumber()) {
+ CharSequence text = mNumber >= 100
+ ? getResources().getString(R.string.unread_convo_overflow, 99)
+ : String.format(Locale.getDefault(), "%d", mNumber);
+ mNumberView.setText(text);
+ mNumberView.setVisibility(VISIBLE);
+ } else {
+ mNumberView.setVisibility(GONE);
+ }
+
+ // changing number can affect the color
+ updateColors();
+ }
+
+ private void updateColors() {
+ if (shouldShowNumber() && !mDisallowColor) {
+ mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor));
+ mIconView.setColorFilter(mHighlightTextColor);
+ mNumberView.setTextColor(mHighlightTextColor);
+ } else {
+ mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor));
+ mIconView.setColorFilter(mDefaultTextColor);
+ mNumberView.setTextColor(mDefaultTextColor);
+ }
+ }
+
+ private boolean shouldShowNumber() {
+ return !mExpanded && mNumber > 1;
+ }
+
+ /**
+ * Set the color used for the expand chevron and the text
+ */
+ @RemotableViewMethod
+ public void setDefaultTextColor(int color) {
+ mDefaultTextColor = color;
+ updateColors();
+ }
+
+ /**
+ * Sets the color used to for the expander when there is no number shown
+ */
+ @RemotableViewMethod
+ public void setDefaultPillColor(@ColorInt int color) {
+ mDefaultPillColor = color;
+ updateColors();
+ }
+
+ /**
+ * Set the color used for the expand chevron and the text
+ */
+ @RemotableViewMethod
+ public void setHighlightTextColor(int color) {
+ mHighlightTextColor = color;
+ updateColors();
+ }
+
+ /**
+ * Sets the color used to highlight the expander when there is a number shown
+ */
+ @RemotableViewMethod
+ public void setHighlightPillColor(@ColorInt int color) {
+ mHighlightPillColor = color;
+ updateColors();
+ }
+
+ /**
+ * Sets the number shown inside the expand button.
+ * This only appears when the expand button is collapsed, and when greater than 1.
+ */
+ @RemotableViewMethod
+ public void setNumber(int number) {
+ if (mNumber != number) {
+ mNumber = number;
+ updateNumber();
+ }
}
}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 1c78750f3610..5e142fd10de0 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -45,6 +45,12 @@ static const char* toString(bool value) {
return value ? "true" : "false";
}
+enum class HandleEventResponse : int {
+ // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent
+ REMOVE_CALLBACK = 0,
+ KEEP_CALLBACK = 1
+};
+
static struct {
jclass clazz;
@@ -70,6 +76,14 @@ static std::string addPrefix(std::string str, std::string_view prefix) {
return str;
}
+/**
+ * Convert an enumeration to its underlying type. Replace with std::to_underlying when available.
+ */
+template <class T>
+static std::underlying_type_t<T> toUnderlying(const T& t) {
+ return static_cast<std::underlying_type_t<T>>(t);
+}
+
class NativeInputEventReceiver : public LooperCallback {
public:
NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -106,9 +120,16 @@ private:
return mInputConsumer.getChannel()->getName();
}
- virtual int handleEvent(int receiveFd, int events, void* data) override;
+ HandleEventResponse processOutboundEvents();
+ // From 'LooperCallback'
+ int handleEvent(int receiveFd, int events, void* data) override;
};
+// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent
+static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>,
+ std::invoke_result_t<decltype(&LooperCallback::handleEvent),
+ NativeInputEventReceiver, int, int, void*>>::value);
+
NativeInputEventReceiver::NativeInputEventReceiver(
JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue)
@@ -179,10 +200,61 @@ void NativeInputEventReceiver::setFdEvents(int events) {
}
}
+/**
+ * Receiver's primary role is to receive input events, but it has an additional duty of sending
+ * 'ack' for events (using the call 'finishInputEvent').
+ *
+ * If we are looking at the communication between InputPublisher and InputConsumer, we can say that
+ * from the InputConsumer's perspective, InputMessage's that are sent from publisher to consumer are
+ * called 'inbound / incoming' events, and the InputMessage's sent from InputConsumer to
+ * InputPublisher are 'outbound / outgoing' events.
+ *
+ * NativeInputEventReceiver owns (and acts like) an InputConsumer. So the finish events are outbound
+ * from InputEventReceiver (and will be sent to the InputPublisher).
+ *
+ * In this function, send as many events from 'mFinishQueue' as possible across the socket to the
+ * InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
+ * unnecessarily.
+ */
+HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
+ while (!mFinishQueue.empty()) {
+ const Finish& finish = *mFinishQueue.begin();
+ status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
+ if (status == OK) {
+ // Successful send. Erase the entry and keep trying to send more
+ mFinishQueue.erase(mFinishQueue.begin());
+ continue;
+ }
+
+ // Publisher is busy, try again later. Keep this entry (do not erase)
+ if (status == WOULD_BLOCK) {
+ if (kDebugDispatchCycle) {
+ ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
+ getInputChannelName().c_str(), mFinishQueue.size());
+ }
+ return HandleEventResponse::KEEP_CALLBACK; // try again later
+ }
+
+ // Some other error. Give up
+ ALOGW("Failed to send outbound event on channel '%s'. status=%d",
+ getInputChannelName().c_str(), status);
+ if (status != DEAD_OBJECT) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ std::string message =
+ android::base::StringPrintf("Failed to send outbound event. status=%d",
+ status);
+ jniThrowRuntimeException(env, message.c_str());
+ mMessageQueue->raiseAndClearException(env, "finishInputEvent");
+ }
+ return HandleEventResponse::REMOVE_CALLBACK;
+ }
+
+ // The queue is now empty. Tell looper there's no more output to expect.
+ setFdEvents(ALOOPER_EVENT_INPUT);
+ return HandleEventResponse::KEEP_CALLBACK;
+}
+
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
- // Allowed return values of this function as documented in LooperCallback::handleEvent
- constexpr int REMOVE_CALLBACK = 0;
- constexpr int KEEP_CALLBACK = 1;
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
// This error typically occurs when the publisher has closed the input channel
// as part of removing a window or finishing an IME session, in which case
@@ -191,56 +263,25 @@ int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data)
ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. "
"events=0x%x", getInputChannelName().c_str(), events);
}
- return REMOVE_CALLBACK;
+ return toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
}
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
- return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
+ return status == OK || status == NO_MEMORY
+ ? toUnderlying(HandleEventResponse::KEEP_CALLBACK)
+ : toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
}
if (events & ALOOPER_EVENT_OUTPUT) {
- for (size_t i = 0; i < mFinishQueue.size(); i++) {
- const Finish& finish = mFinishQueue[i];
- status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
- if (status != OK) {
- mFinishQueue.erase(mFinishQueue.begin(), mFinishQueue.begin() + i);
-
- if (status == WOULD_BLOCK) {
- if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
- getInputChannelName().c_str(), i, mFinishQueue.size());
- }
- return KEEP_CALLBACK; // try again later
- }
-
- ALOGW("Failed to send finished signal on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
- if (status != DEAD_OBJECT) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- std::string message =
- android::base::StringPrintf("Failed to finish input event. status=%d",
- status);
- jniThrowRuntimeException(env, message.c_str());
- mMessageQueue->raiseAndClearException(env, "finishInputEvent");
- }
- return REMOVE_CALLBACK;
- }
- }
- if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
- getInputChannelName().c_str(), mFinishQueue.size());
- }
- mFinishQueue.clear();
- setFdEvents(ALOOPER_EVENT_INPUT);
- return KEEP_CALLBACK;
+ return toUnderlying(processOutboundEvents());
}
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x", getInputChannelName().c_str(), events);
- return KEEP_CALLBACK;
+ return toUnderlying(HandleEventResponse::KEEP_CALLBACK);
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ecb7c1d30f83..50d1e6bb0b18 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1886,6 +1886,11 @@
<permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows system APK to manage country code.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_WIFI_COUNTRY_CODE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @hide Allows an application to manage an automotive device's application network
preference as it relates to OEM_PAID and OEM_PRIVATE capable networks.
<p>Not for use by third-party or privileged applications. -->
diff --git a/core/res/res/color/text_color_primary_device_default_dark.xml b/core/res/res/color/text_color_primary_device_default_dark.xml
new file mode 100644
index 000000000000..90d6b07b24bd
--- /dev/null
+++ b/core/res/res/color/text_color_primary_device_default_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see primary_text_material_dark.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_50"/>
+ <item android:color="@color/system_primary_50"/>
+</selector>
diff --git a/core/res/res/color/text_color_primary_device_default_light.xml b/core/res/res/color/text_color_primary_device_default_light.xml
new file mode 100644
index 000000000000..bdc4fa92b2f2
--- /dev/null
+++ b/core/res/res/color/text_color_primary_device_default_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see primary_text_material_light.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_900"/>
+ <item android:color="@color/system_primary_900"/>
+</selector>
diff --git a/core/res/res/color/text_color_secondary_device_default_dark.xml b/core/res/res/color/text_color_secondary_device_default_dark.xml
new file mode 100644
index 000000000000..799636addd4b
--- /dev/null
+++ b/core/res/res/color/text_color_secondary_device_default_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see secondary_text_material_dark.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_200"/>
+ <item android:color="@color/system_primary_200"/>
+</selector>
diff --git a/core/res/res/color/text_color_secondary_device_default_light.xml b/core/res/res/color/text_color_secondary_device_default_light.xml
new file mode 100644
index 000000000000..4793bb8e0360
--- /dev/null
+++ b/core/res/res/color/text_color_secondary_device_default_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see secondary_text_material_light.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_700"/>
+ <item android:color="@color/system_primary_700"/>
+</selector>
diff --git a/core/res/res/color/text_color_tertiary_device_default_dark.xml b/core/res/res/color/text_color_tertiary_device_default_dark.xml
new file mode 100644
index 000000000000..c82863109e7d
--- /dev/null
+++ b/core/res/res/color/text_color_tertiary_device_default_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see tertiary_text_material_dark.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_400"/>
+ <item android:color="@color/system_primary_400"/>
+</selector>
diff --git a/core/res/res/color/text_color_tertiary_device_default_light.xml b/core/res/res/color/text_color_tertiary_device_default_light.xml
new file mode 100644
index 000000000000..82c420ad97fc
--- /dev/null
+++ b/core/res/res/color/text_color_tertiary_device_default_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Please see tertiary_text_material_light.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="@color/system_primary_500"/>
+ <item android:color="@color/system_primary_500"/>
+</selector>
diff --git a/core/res/res/drawable/conversation_unread_bg.xml b/core/res/res/drawable/expand_button_pill_bg.xml
index d3e00cfbf8b1..f95044a7fe76 100644
--- a/core/res/res/drawable/conversation_unread_bg.xml
+++ b/core/res/res/drawable/expand_button_pill_bg.xml
@@ -14,6 +14,6 @@
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <corners android:radius="20sp" />
+ <corners android:radius="@dimen/notification_expand_button_pill_height" />
<solid android:color="@android:color/white" />
</shape> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_collapse_notification.xml b/core/res/res/drawable/ic_collapse_notification.xml
index ca4f0ed27a0b..a06ec9fc813f 100644
--- a/core/res/res/drawable/ic_collapse_notification.xml
+++ b/core/res/res/drawable/ic_collapse_notification.xml
@@ -21,8 +21,5 @@ Copyright (C) 2020 The Android Open Source Project
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
- android:pathData="M18.59,16.41L20.0,15.0l-8.0,-8.0 -8.0,8.0 1.41,1.41L12.0,9.83"/>
- <path
- android:pathData="M0 0h24v24H0V0z"
- android:fillColor="#00000000"/>
+ android:pathData="M18.59,15.41L20.0,14.0l-8.0,-8.0 -8.0,8.0 1.41,1.41L12.0,8.83"/>
</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_expand_notification.xml b/core/res/res/drawable/ic_expand_notification.xml
index a080ce43cfec..160a9c2c1970 100644
--- a/core/res/res/drawable/ic_expand_notification.xml
+++ b/core/res/res/drawable/ic_expand_notification.xml
@@ -21,8 +21,5 @@ Copyright (C) 2014 The Android Open Source Project
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
- android:pathData="M5.41,7.59L4.0,9.0l8.0,8.0 8.0,-8.0 -1.41,-1.41L12.0,14.17"/>
- <path
- android:pathData="M24 24H0V0h24v24z"
- android:fillColor="#00000000"/>
+ android:pathData="M5.41,8.59L4.0,10.0l8.0,8.0 8.0,-8.0 -1.41,-1.41L12.0,15.17"/>
</vector> \ No newline at end of file
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
new file mode 100644
index 000000000000..f92e6d63cbe7
--- /dev/null
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<com.android.internal.widget.NotificationExpandButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:contentDescription="@string/expand_button_content_description_collapsed"
+ android:padding="16dp"
+ android:visibility="gone"
+ >
+
+ <LinearLayout
+ android:id="@+id/expand_button_pill"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_expand_button_pill_height"
+ android:orientation="horizontal"
+ android:background="@drawable/expand_button_pill_bg"
+ >
+
+ <TextView
+ android:id="@+id/expand_button_number"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_expand_button_pill_height"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ />
+
+ <ImageView
+ android:id="@+id/expand_button_icon"
+ android:layout_width="@dimen/notification_expand_button_pill_height"
+ android:layout_height="@dimen/notification_expand_button_pill_height"
+ android:padding="2dp"
+ android:scaleType="fitCenter"
+ android:importantForAccessibility="no"
+ />
+
+ </LinearLayout>
+
+</com.android.internal.widget.NotificationExpandButton>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 1de1d049197c..81a79c50c3ef 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -30,7 +30,8 @@
android:id="@+id/left_icon"
android:layout_width="@dimen/notification_left_icon_size"
android:layout_height="@dimen/notification_left_icon_size"
- android:layout_gravity="center_vertical|start"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
android:layout_marginStart="@dimen/notification_left_icon_start"
android:background="@drawable/notification_large_icon_outline"
android:clipToOutline="true"
@@ -43,7 +44,8 @@
android:id="@+id/icon"
android:layout_width="@dimen/notification_icon_circle_size"
android:layout_height="@dimen/notification_icon_circle_size"
- android:layout_gravity="center_vertical|start"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
android:layout_marginStart="@dimen/notification_icon_circle_start"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_icon_circle_padding"
@@ -55,10 +57,12 @@
android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_toStartOf="@id/expand_button"
+ android:layout_alignWithParentIfMissing="true"
android:clipChildren="false"
android:gravity="center_vertical"
- android:paddingEnd="@dimen/notification_heading_margin_end"
android:paddingStart="@dimen/notification_content_margin_start"
android:theme="@style/Theme.DeviceDefault.Notification"
>
@@ -71,19 +75,15 @@
android:id="@+id/alternate_expand_target"
android:layout_width="@dimen/notification_content_margin_start"
android:layout_height="match_parent"
- android:layout_gravity="start"
+ android:layout_alignParentStart="true"
android:importantForAccessibility="no"
/>
- <com.android.internal.widget.NotificationExpandButton
- android:id="@+id/expand_button"
- android:layout_width="@dimen/notification_header_expand_icon_size"
- android:layout_height="@dimen/notification_header_expand_icon_size"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/expand_button_content_description_collapsed"
- android:paddingTop="@dimen/notification_expand_button_padding_top"
- android:scaleType="center"
- android:visibility="gone"
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
/>
</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index b83611bcc177..bad9a6ba6184 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -74,14 +74,10 @@
android:layout_height="match_parent"
android:layout_gravity="end">
- <com.android.internal.widget.NotificationExpandButton
- android:id="@+id/expand_button"
- android:layout_width="@dimen/notification_header_expand_icon_size"
- android:layout_height="@dimen/notification_header_expand_icon_size"
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/expand_button_content_description_collapsed"
- android:paddingTop="@dimen/notification_expand_button_padding_top"
- android:scaleType="center"
/>
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
index 471d874c59f5..7b52ec30abe6 100644
--- a/core/res/res/layout/notification_template_material_call.xml
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -72,15 +72,10 @@
</LinearLayout>
<!-- TODO(b/179178086): remove padding from main column when this is visible -->
- <com.android.internal.widget.NotificationExpandButton
- android:id="@+id/expand_button"
- android:layout_width="@dimen/notification_header_expand_icon_size"
- android:layout_height="@dimen/notification_header_expand_icon_size"
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="top|end"
- android:contentDescription="@string/expand_button_content_description_collapsed"
- android:paddingTop="@dimen/notification_expand_button_padding_top"
- android:scaleType="center"
- android:visibility="gone"
/>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index f3aa54066c92..42fb4a26dd3b 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -104,11 +104,10 @@
<LinearLayout
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_size"
- android:paddingStart="@dimen/conversation_expand_button_side_margin"
+ android:layout_height="@dimen/conversation_expand_button_height"
android:orientation="horizontal"
android:layout_gravity="end|top"
- android:paddingEnd="@dimen/conversation_expand_button_side_margin"
+ android:paddingEnd="0dp"
android:clipToPadding="false"
android:clipChildren="false"
>
@@ -118,34 +117,16 @@
android:forceHasOverlappingRendering="false"
android:layout_width="40dp"
android:layout_height="40dp"
- android:layout_marginEnd="11dp"
+ android:layout_marginStart="@dimen/conversation_image_start_margin"
android:spacing="0dp"
android:layout_gravity="center"
android:clipToPadding="false"
android:clipChildren="false"
/>
- <!-- Unread Count -->
- <TextView
- android:id="@+id/conversation_unread_count"
- android:layout_width="33sp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="11dp"
- android:layout_gravity="center"
- android:gravity="center"
- android:padding="2dp"
- android:visibility="gone"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
- android:textColor="#FFFFFF"
- android:textSize="12sp"
- android:background="@drawable/conversation_unread_bg"
- />
- <com.android.internal.widget.NotificationExpandButton
- android:id="@+id/expand_button"
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:drawable="@drawable/ic_expand_notification"
- android:contentDescription="@string/expand_button_content_description_collapsed"
/>
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 45e11ba9820e..bed5c31bd327 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1773,6 +1773,8 @@
<enum name="maps" value="6" />
<!-- Apps which are primarily productivity apps, such as cloud storage or workplace apps. -->
<enum name="productivity" value="7" />
+ <!-- Apps which are primarily accessibility apps, such as screen-readers. -->
+ <enum name="accessibility" value="8" />
</attr>
<!-- Declares the kind of classloader this application's classes must be loaded with -->
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 1020c14ef665..9b5632174e44 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -42,12 +42,7 @@
<color name="background_floating_device_default_dark">@color/system_primary_900</color>
<color name="background_floating_device_default_light">@color/system_primary_100</color>
- <color name="text_color_primary_device_default_light">@color/system_primary_900</color>
- <color name="text_color_primary_device_default_dark">@color/system_primary_50</color>
- <color name="text_color_secondary_device_default_light">@color/system_primary_700</color>
- <color name="text_color_secondary_device_default_dark">@color/system_primary_200</color>
- <color name="text_color_tertiary_device_default_light">@color/system_primary_500</color>
- <color name="text_color_tertiary_device_default_dark">@color/system_primary_400</color>
+ <!-- Please refer to text_color_[primary]_device_default_[light].xml for text colors-->
<color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color>
<color name="foreground_device_default_dark">@color/text_color_primary_device_default_dark</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b0a4c6e027cd..d61d19a41266 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -673,15 +673,6 @@
-->
</integer-array>
- <!-- The device states (supplied by DeviceStateManager) that should be treated as unfolded by
- the display fold controller. Default is empty. -->
- <integer-array name="config_unfoldedDeviceStates">
- <!-- Example:
- <item>3</item>
- <item>4</item>
- -->
- </integer-array>
-
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>
@@ -3957,6 +3948,10 @@
color supplied by the Notification.Builder if present. -->
<bool name="config_tintNotificationActionButtons">true</bool>
+ <!-- Flag indicating that tinted items (actions, expander, etc) are to be tinted using the
+ theme color, rather than the notification color. -->
+ <bool name="config_tintNotificationsWithTheme">true</bool>
+
<!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
<bool name="config_showAreaUpdateInfoSettings">false</bool>
@@ -4671,15 +4666,6 @@
<!-- WindowsManager JetPack display features -->
<string name="config_display_features" translatable="false" />
- <!-- Physical Display IDs of the display-devices that are swapped when a folding device folds.
- This list is expected to contain two elements: the first is the display to use
- when the device is folded, the second is the display to use when unfolded. If the array
- is empty or the display IDs are not recognized, this feature is turned off and the value
- ignored.
- TODO: b/170470621 - remove once we can have multiple Internal displays in DMS as
- well as a notification from DisplayStateManager. -->
- <string-array name="config_internalFoldedPhysicalDisplayIds" translatable="false" />
-
<!-- Aspect ratio of task level letterboxing. Values <= 1.0 will be ignored.
Note: Activity min/max aspect ratio restrictions will still be respected by the
activity-level letterboxing (size-compat mode). Therefore this override can control the
@@ -4710,6 +4696,14 @@
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
+ <!-- The timeout value in milliseconds used by SelectionActionModeHelper for each selections
+ when TextClassifier has been initialized. -->
+ <integer name="config_smartSelectionInitializedTimeoutMillis">200</integer>
+
+ <!-- The timeout value in milliseconds used by SelectionActionModeHelper for each selections
+ when TextClassifier has not been initialized. -->
+ <integer name="config_smartSelectionInitializingTimeoutMillis">500</integer>
+
<!-- Indicates that default fitness tracker app needs to request sensor and location permissions. -->
<bool name="config_trackerAppNeedsPermissions">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c2b6b99dcc1c..10aa7b3f4354 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -224,7 +224,7 @@
<dimen name="notification_content_margin_end">16dp</dimen>
<!-- The margin on the end of the top-line content views (accommodates the expander) -->
- <dimen name="notification_heading_margin_end">48dp</dimen>
+ <dimen name="notification_heading_margin_end">56dp</dimen>
<!-- The margin for text at the end of the image view for media notifications -->
<dimen name="notification_media_image_margin_end">72dp</dimen>
@@ -248,7 +248,7 @@
<dimen name="call_notification_collapsible_indent">64dp</dimen>
<!-- The size of icons for visual actions in the notification_material_action_list -->
- <dimen name="notification_actions_icon_size">48dp</dimen>
+ <dimen name="notification_actions_icon_size">56dp</dimen>
<!-- The size of icons for visual actions in the notification_material_action_list -->
<dimen name="notification_actions_icon_drawable_size">20dp</dimen>
@@ -314,10 +314,10 @@
<dimen name="notification_conversation_header_separating_margin">4dp</dimen>
<!-- The absolute size of the notification expand icon. -->
- <dimen name="notification_header_expand_icon_size">48dp</dimen>
+ <dimen name="notification_header_expand_icon_size">56dp</dimen>
- <!-- The top padding for the notification expand button. -->
- <dimen name="notification_expand_button_padding_top">1dp</dimen>
+ <!-- the height of the expand button pill -->
+ <dimen name="notification_expand_button_pill_height">24dp</dimen>
<!-- Vertical margin for the headerless notification content, when content has 1 line -->
<!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
@@ -690,6 +690,13 @@
<!-- The default minimal size of a PiP task, in both dimensions. -->
<dimen name="default_minimal_size_pip_resizable_task">108dp</dimen>
+ <!--
+ The overridable minimal size of a PiP task, in both dimensions.
+ Different from default_minimal_size_pip_resizable_task, this is to limit the dimension
+ when the pinned stack size is overridden by app via minWidth/minHeight.
+ -->
+ <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
+
<!-- Height of a task when in minimized mode from the top when launcher is resizable. -->
<dimen name="task_height_of_minimized_mode">80dp</dimen>
@@ -739,7 +746,7 @@
<dimen name="notification_right_icon_headerless_margin">20dp</dimen>
<!-- The top margin of the right icon in the "big" notification states -->
<!-- TODO(b/181048615): Move the large icon below the expander in big states -->
- <dimen name="notification_right_icon_big_margin_top">16dp</dimen>
+ <dimen name="notification_right_icon_big_margin_top">20dp</dimen>
<!-- The size of the left icon -->
<dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen>
<!-- The left padding of the left icon -->
@@ -770,13 +777,10 @@
<dimen name="conversation_icon_circle_start">28dp</dimen>
<!-- Start of the content in the conversation template -->
<dimen name="conversation_content_start">80dp</dimen>
- <!-- Size of the expand button in the conversation layout -->
- <dimen name="conversation_expand_button_size">80dp</dimen>
- <!-- Top margin of the expand button for conversations when expanded -->
- <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen>
- <!-- Side margin of the expand button for conversations.
- width of expand asset (22) + 2 * this (13) == notification_header_expand_icon_size (48) -->
- <dimen name="conversation_expand_button_side_margin">13dp</dimen>
+ <!-- Height of the expand button in the conversation layout -->
+ <dimen name="conversation_expand_button_height">80dp</dimen>
+ <!-- this is the margin between the Conversation image and the content -->
+ <dimen name="conversation_image_start_margin">12dp</dimen>
<!-- Side margins of the conversation badge in relation to the conversation icon -->
<dimen name="conversation_badge_side_margin">36dp</dimen>
<!-- size of the notification badge when applied to the conversation icon -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 71ba44b0ded1..2b1168f14f21 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1524,8 +1524,15 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_mediaLocation">Allows the app to read locations from your media collection.</string>
+ <!-- Name for an app setting that lets the user authenticate for that app using biometrics (e.g. fingerprint or face). [CHAR LIMIT=30] -->
+ <string name="biometric_app_setting_name">Use biometrics</string>
+ <!-- Name for an app setting that lets the user authenticate for that app using biometrics (e.g. fingerprint or face) or their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+ <string name="biometric_or_screen_lock_app_setting_name">Use biometrics or screen lock</string>
<!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
<string name="biometric_dialog_default_title">Verify it\u2019s you</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face). [CHAR LIMIT=70] -->
+ <string name="biometric_dialog_default_subtitle">Use your biometric to continue</string>
+
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
<!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
@@ -1539,6 +1546,11 @@
<!-- Message returned to applications when an unexpected/unknown error occurs. [CHAR LIMIT=50]-->
<string name="biometric_error_generic">Error authenticating</string>
+ <!-- Name for an app setting that lets the user authenticate for that app with their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=30] -->
+ <string name="screen_lock_app_setting_name">Use screen lock</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+ <string name="screen_lock_dialog_default_subtitle">Enter your device credential to continue</string>
+
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
@@ -1585,6 +1597,11 @@
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+
+ <!-- Name for an app setting that lets the user authenticate for that app with their fingerprint. [CHAR LIMIT=30] -->
+ <string name="fingerprint_app_setting_name">Use fingerprint</string>
+ <!-- Name for an app setting that lets the user authenticate for that app with their fingerprint or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+ <string name="fingerprint_or_screen_lock_app_setting_name">Use fingerprint or screen lock</string>
<!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
<string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
@@ -1681,6 +1698,13 @@
<!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
<string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
+ <!-- Name for an app setting that lets the user authenticate for that app with their face. [CHAR LIMIT=30] -->
+ <string name="face_app_setting_name">Use face unlock</string>
+ <!-- Name for an app setting that lets the user authenticate for that app with their face or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+ <string name="face_or_screen_lock_app_setting_name">Use face or screen lock</string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their face. [CHAR LIMIT=70] -->
+ <string name="face_dialog_default_subtitle">Use face unlock to continue</string>
+
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="face_error_vendor">
</string-array>
@@ -5193,6 +5217,8 @@
<string name="app_category_maps">Maps &amp; Navigation</string>
<!-- Category title for apps which are primarily productivity apps, such as cloud storage or workplace apps. [CHAR LIMIT=32] -->
<string name="app_category_productivity">Productivity</string>
+ <!-- Category title for apps which are primarily accessibility apps, such as screen-readers. [CHAR LIMIT=32] -->
+ <string name="app_category_accessibility">Accessibility</string>
<!-- Channel name for DeviceStorageMonitor notifications -->
<string name="device_storage_monitor_notification_channel">Device storage</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index aecb01551a26..ff9d26fb2363 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -482,6 +482,8 @@
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
<java-symbol type="string" name="config_bandwidthEstimateSource" />
+ <java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" />
+ <java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -1893,6 +1895,7 @@
<java-symbol type="bool" name="config_notificationHeaderClickableForExpand" />
<java-symbol type="bool" name="config_enableNightMode" />
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
+ <java-symbol type="bool" name="config_tintNotificationsWithTheme" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
@@ -1948,6 +1951,7 @@
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
<java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" />
+ <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" />
<java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" />
@@ -2468,7 +2472,10 @@
<java-symbol type="string" name="config_keyguardComponent" />
<!-- Biometric messages -->
+ <java-symbol type="string" name="biometric_app_setting_name" />
+ <java-symbol type="string" name="biometric_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="biometric_dialog_default_title" />
+ <java-symbol type="string" name="biometric_dialog_default_subtitle" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
<java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
@@ -2476,6 +2483,10 @@
<java-symbol type="string" name="biometric_error_device_not_secured" />
<java-symbol type="string" name="biometric_error_generic" />
+ <!-- Device credential strings for BiometricManager -->
+ <java-symbol type="string" name="screen_lock_app_setting_name" />
+ <java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
+
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
<java-symbol type="string" name="fingerprint_error_hw_not_available" />
@@ -2493,6 +2504,8 @@
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
+ <java-symbol type="string" name="fingerprint_app_setting_name" />
+ <java-symbol type="string" name="fingerprint_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
@@ -2540,6 +2553,9 @@
<java-symbol type="string" name="face_acquired_sensor_dirty" />
<java-symbol type="array" name="face_acquired_vendor" />
<java-symbol type="string" name="face_name_template" />
+ <java-symbol type="string" name="face_app_setting_name" />
+ <java-symbol type="string" name="face_or_screen_lock_app_setting_name" />
+ <java-symbol type="string" name="face_dialog_default_subtitle" />
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
@@ -2892,6 +2908,9 @@
<java-symbol type="id" name="header_text" />
<java-symbol type="id" name="header_text_secondary" />
<java-symbol type="id" name="expand_button" />
+ <java-symbol type="id" name="expand_button_pill" />
+ <java-symbol type="id" name="expand_button_number" />
+ <java-symbol type="id" name="expand_button_icon" />
<java-symbol type="id" name="alternate_expand_target" />
<java-symbol type="id" name="notification_header" />
<java-symbol type="id" name="notification_top_line" />
@@ -2912,7 +2931,6 @@
<java-symbol type="dimen" name="notification_header_background_height" />
<java-symbol type="dimen" name="notification_header_touchable_height" />
<java-symbol type="dimen" name="notification_header_expand_icon_size" />
- <java-symbol type="dimen" name="notification_expand_button_padding_top" />
<java-symbol type="dimen" name="notification_header_icon_size" />
<java-symbol type="dimen" name="notification_header_app_name_margin_start" />
<java-symbol type="dimen" name="notification_header_separating_margin" />
@@ -3296,6 +3314,7 @@
<java-symbol type="string" name="app_category_news" />
<java-symbol type="string" name="app_category_maps" />
<java-symbol type="string" name="app_category_productivity" />
+ <java-symbol type="string" name="app_category_accessibility" />
<java-symbol type="raw" name="fallback_categories" />
@@ -3765,7 +3784,6 @@
<!-- For Foldables -->
<java-symbol type="array" name="config_foldedDeviceStates" />
- <java-symbol type="array" name="config_unfoldedDeviceStates" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
@@ -4033,7 +4051,6 @@
<java-symbol type="id" name="message_icon_container" />
<java-symbol type="id" name="conversation_image_message_container" />
<java-symbol type="id" name="conversation_icon_container" />
- <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" />
<java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" />
<java-symbol type="dimen" name="conversation_badge_side_margin" />
<java-symbol type="dimen" name="conversation_avatar_size" />
@@ -4054,7 +4071,6 @@
<java-symbol type="dimen" name="button_padding_horizontal_material" />
<java-symbol type="dimen" name="button_inset_horizontal_material" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
- <java-symbol type="id" name="conversation_unread_count" />
<java-symbol type="string" name="unread_convo_overflow" />
<java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" />
<java-symbol type="drawable" name="conversation_badge_background" />
@@ -4161,7 +4177,6 @@
<java-symbol type="dimen" name="default_background_blur_radius" />
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
- <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 3706e4b3d8e8..b0c1f25ad030 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.net.TetheringManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -141,7 +142,7 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase {
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ mIntentFilter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
mContext.registerReceiver(mWifiReceiver, mIntentFilter);
logv("Clear Wifi before we start the test.");
diff --git a/core/tests/coretests/src/android/app/usage/OWNERS b/core/tests/coretests/src/android/app/usage/OWNERS
new file mode 100644
index 000000000000..1271fa799808
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 532296
+include /services/usage/OWNERS
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 4d04a7af4693..8de9454ddeda 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -78,15 +78,15 @@ public class UsageStatsPersistenceTest {
"VALID_FLAG_BITS", "UNASSIGNED_TOKEN", "MAX_EVENT_TYPE"};
// All fields in this list are final constants defining event types and not persisted
private static final String[] EVENT_TYPES = {"NONE", "ACTIVITY_DESTROYED", "ACTIVITY_PAUSED",
- "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "CHOOSER_ACTION", "CONFIGURATION_CHANGE",
- "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE", "DEVICE_SHUTDOWN",
- "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK", "FOREGROUND_SERVICE_START",
- "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN", "KEYGUARD_SHOWN", "LOCUS_ID_SET",
- "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND", "NOTIFICATION_INTERRUPTION",
- "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE", "SCREEN_INTERACTIVE",
- "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED", "SLICE_PINNED_PRIV",
- "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION", "USER_STOPPED",
- "USER_UNLOCKED"};
+ "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "APP_COMPONENT_USED", "CHOOSER_ACTION",
+ "CONFIGURATION_CHANGE", "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE",
+ "DEVICE_SHUTDOWN", "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK",
+ "FOREGROUND_SERVICE_START", "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN",
+ "KEYGUARD_SHOWN", "LOCUS_ID_SET", "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND",
+ "NOTIFICATION_INTERRUPTION", "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE",
+ "SCREEN_INTERACTIVE", "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED",
+ "SLICE_PINNED_PRIV", "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION",
+ "USER_STOPPED", "USER_UNLOCKED"};
@Test
public void testUsageEventsFields() {
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index eae41e37e5f3..7bc81cd2f928 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -26,6 +26,8 @@ import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static junit.framework.Assert.fail;
+
import android.graphics.fonts.FontStyle;
import android.os.LocaleList;
import android.text.FontConfig;
@@ -44,6 +46,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -221,9 +224,113 @@ public final class FontListParserTest {
.that(readFamily(serialized)).isEqualTo(expected);
}
+ @Test
+ public void invalidXml_unpaired_family() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc</font>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unpaired_font() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unpaired_axis() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " <axis tag=\"wght\" styleValue=\"0\" >"
+ + " </font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_family() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'"
+ + " <font index='0'>test.ttc</font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_font() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_axis() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " <axis tag=\"wght\" styleValue=\"0\""
+ + " </font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
private FontConfig.FontFamily readFamily(String xml)
throws IOException, XmlPullParserException {
- StandardCharsets.UTF_8.name();
ByteArrayInputStream buffer = new ByteArrayInputStream(
xml.getBytes(StandardCharsets.UTF_8));
XmlPullParser parser = Xml.newPullParser();
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 89411902bb6b..c06405affc1b 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -20,6 +20,7 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
import org.junit.Test;
@@ -33,16 +34,16 @@ public class VibratorInfoTest {
@Test
public void testHasAmplitudeControl() {
assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
- assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS
- | VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL).hasAmplitudeControl());
+ assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
+ | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
}
@Test
public void testHasCapabilities() {
- assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
- .hasCapability(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS));
- assertFalse(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
- .hasCapability(VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL));
+ assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+ .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+ assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+ .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
}
@Test
@@ -59,7 +60,7 @@ public class VibratorInfoTest {
@Test
public void testIsPrimitiveSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -73,30 +74,30 @@ public class VibratorInfoTest {
@Test
public void testEquals() {
VibratorInfo empty = new VibratorInfo(1, 0, null, null);
- VibratorInfo complete = new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
assertEquals(complete, complete);
- assertEquals(complete, new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK}, null)));
}
@Test
public void testSerialization() {
- VibratorInfo original = new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
new int[]{VibrationEffect.EFFECT_CLICK}, null);
Parcel parcel = Parcel.obtain();
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 3d964fb9bb87..f2a33de008d6 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -70,5 +70,6 @@
<permission name="android.permission.READ_WIFI_CREDENTIAL" />
<permission name="android.permission.USE_BACKGROUND_BLUR" />
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+ <permission name="android.permission.FORCE_STOP_PACKAGES" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index ea42246e8262..77a38a9bb1a0 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -166,6 +166,7 @@
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
<assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
+ <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="audioserver" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
@@ -176,6 +177,7 @@
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
<assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
+ <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index cabfad44cfb9..b7bf8ab75799 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -43,6 +43,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-2093859262": {
+ "message": "setClientVisible: %s clientVisible=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
"-2072089308": {
"message": "Attempted to add window with token that is a sub-window: %s. Aborting.",
"level": "WARN",
@@ -811,6 +817,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-1159577965": {
+ "message": "Focus requested for input consumer=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/InputMonitor.java"
+ },
"-1156118957": {
"message": "Updated config=%s",
"level": "DEBUG",
@@ -823,12 +835,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
},
- "-1144293044": {
- "message": "SURFACE SET FREEZE LAYER: %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
- },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
@@ -1831,6 +1837,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "63329306": {
+ "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WallpaperWindowToken.java"
+ },
"73987756": {
"message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
"level": "INFO",
@@ -2461,6 +2473,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "691515534": {
+ "message": " Commit wallpaper becoming invisible: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"693423992": {
"message": "setAnimationLocked: setting mFocusMayChange true",
"level": "INFO",
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 63df0db99764..95c7715a1688 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -136,7 +136,7 @@ public class FontListParser {
customization.getAdditionalNamedFamilies();
parser.require(XmlPullParser.START_TAG, null, "familyset");
- while (parser.next() != XmlPullParser.END_TAG) {
+ while (keepReading(parser)) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
@@ -158,6 +158,12 @@ public class FontListParser {
return new FontConfig(families, aliases, lastModifiedDate, configVersion);
}
+ private static boolean keepReading(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int next = parser.next();
+ return next != XmlPullParser.END_TAG && next != XmlPullParser.END_DOCUMENT;
+ }
+
/**
* Read family tag in fonts.xml or oem_customization.xml
*/
@@ -168,7 +174,7 @@ public class FontListParser {
final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final List<FontConfig.Font> fonts = new ArrayList<>();
- while (parser.next() != XmlPullParser.END_TAG) {
+ while (keepReading(parser)) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
final String tag = parser.getName();
if (tag.equals(TAG_FONT)) {
@@ -232,7 +238,7 @@ public class FontListParser {
boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
StringBuilder filename = new StringBuilder();
- while (parser.next() != XmlPullParser.END_TAG) {
+ while (keepReading(parser)) {
if (parser.getEventType() == XmlPullParser.TEXT) {
filename.append(parser.getText());
}
@@ -359,6 +365,8 @@ public class FontListParser {
case XmlPullParser.END_TAG:
depth--;
break;
+ case XmlPullParser.END_DOCUMENT:
+ return;
}
}
}
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
new file mode 100644
index 000000000000..41cfb2707fcf
--- /dev/null
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.vpnprofilestore.IVpnProfileStore;
+import android.util.Log;
+
+/**
+ * @hide This class allows legacy VPN access to its profiles that were stored in Keystore.
+ * The storage of unstructured blobs in Android Keystore is going away, because there is no
+ * architectural or security benefit of storing profiles in keystore over storing them
+ * in the file system. This class allows access to the blobs that still exist in keystore.
+ * And it stores new blob in a database that is still owned by Android Keystore.
+ */
+public class LegacyVpnProfileStore {
+ private static final String TAG = "LegacyVpnProfileStore";
+
+ public static final int SYSTEM_ERROR = IVpnProfileStore.ERROR_SYSTEM_ERROR;
+ public static final int PROFILE_NOT_FOUND = IVpnProfileStore.ERROR_PROFILE_NOT_FOUND;
+
+ private static final String VPN_PROFILE_STORE_SERVICE_NAME = "android.security.vpnprofilestore";
+
+ private static IVpnProfileStore getService() {
+ return IVpnProfileStore.Stub.asInterface(
+ ServiceManager.checkService(VPN_PROFILE_STORE_SERVICE_NAME));
+ }
+
+ /**
+ * Stores the profile under the alias in the profile database. Existing profiles by the
+ * same name will be replaced.
+ * @param alias The name of the profile
+ * @param profile The profile.
+ * @return true if the profile was successfully added. False otherwise.
+ * @hide
+ */
+ public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ getService().put(alias, profile);
+ return true;
+ } else {
+ return KeyStore.getInstance().put(
+ alias, profile, KeyStore.UID_SELF, 0);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to put vpn profile.", e);
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves a profile by the name alias from the profile database.
+ * @param alias Name of the profile to retrieve.
+ * @return The unstructured blob, that is the profile that was stored using
+ * LegacyVpnProfileStore#put or with
+ * android.security.Keystore.put(Credentials.VPN + alias).
+ * Returns null if no profile was found.
+ * @hide
+ */
+ public static byte[] get(@NonNull String alias) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ return getService().get(alias);
+ } else {
+ return KeyStore.getInstance().get(alias, true /* suppressKeyNotFoundWarning */);
+ }
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode != PROFILE_NOT_FOUND) {
+ Log.e(TAG, "Failed to get vpn profile.", e);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get vpn profile.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Removes a profile by the name alias from the profile database.
+ * @param alias Name of the profile to be removed.
+ * @return True if a profile was removed. False if no such profile was found.
+ * @hide
+ */
+ public static boolean remove(@NonNull String alias) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ getService().remove(alias);
+ return true;
+ } else {
+ return KeyStore.getInstance().delete(alias);
+ }
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode != PROFILE_NOT_FOUND) {
+ Log.e(TAG, "Failed to remove vpn profile.", e);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to remove vpn profile.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Lists the vpn profiles stored in the database.
+ * @return An array of strings representing the aliases stored in the profile database.
+ * The return value may be empty but never null.
+ * @hide
+ */
+ public static @NonNull String[] list(@NonNull String prefix) {
+ try {
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ final String[] aliases = getService().list(prefix);
+ for (int i = 0; i < aliases.length; ++i) {
+ aliases[i] = aliases[i].substring(prefix.length());
+ }
+ return aliases;
+ } else {
+ final String[] result = KeyStore.getInstance().list(prefix);
+ return result != null ? result : new String[0];
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to list vpn profiles.", e);
+ }
+ return new String[0];
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1320780bfb8f..d31e637b778e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -327,6 +327,10 @@ public class BubbleController {
return mImpl;
}
+ public ShellExecutor getMainExecutor() {
+ return mMainExecutor;
+ }
+
/**
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 2f31acd41408..9ef3fb54fb35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -340,7 +340,7 @@ public class BubbleExpandedView extends LinearLayout {
mSettingsIcon.setVisibility(GONE);
} else {
mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
- mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
+ mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index ac5d14c802d8..702385ecc3d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -54,6 +54,7 @@ public class PipBoundsAlgorithm {
private float mMaxAspectRatio;
private int mDefaultStackGravity;
private int mDefaultMinSize;
+ private int mOverridableMinSize;
private Point mScreenEdgeInsets;
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) {
@@ -78,6 +79,8 @@ public class PipBoundsAlgorithm {
com.android.internal.R.integer.config_defaultPictureInPictureGravity);
mDefaultMinSize = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
+ mOverridableMinSize = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task);
final String screenEdgeInsetsDpString = res.getString(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
@@ -155,7 +158,10 @@ public class PipBoundsAlgorithm {
// -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
// without minWidth/minHeight
if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
- return new Size(windowLayout.minWidth, windowLayout.minHeight);
+ // If either dimension is smaller than the allowed minimum, adjust them
+ // according to mOverridableMinSize
+ return new Size(Math.max(windowLayout.minWidth, mOverridableMinSize),
+ Math.max(windowLayout.minHeight, mOverridableMinSize));
}
return null;
}
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 d10c03677d30..79ec624a1557 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
@@ -43,7 +43,6 @@ import com.android.wm.shell.ShellTestCase;
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.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
@@ -125,7 +124,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void startSwipePipToHome_updatesOverrideMinSize() {
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
createPipParams(null));
@@ -153,7 +152,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskAppeared_updatesOverrideMinSize() {
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.onTaskAppeared(
createTaskInfo(mComponent1, createPipParams(null), minSize),
@@ -191,7 +190,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), null /* leash */);
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null), minSize));
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 77ceda91898e..d663c52b2c08 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -421,7 +421,6 @@ cc_defaults {
"libstatspull",
"libstatssocket",
"libpdfium",
- "libbinder_ndk",
],
static_libs: [
"libgif",
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 912d04c5d87d..e9b2f4a9bb3b 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -80,6 +80,10 @@ public:
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
+ // The struct is zeroed by memset above. That also sets FrameInfoIndex::InputEventId to
+ // equal android::os::IInputConstants::INVALID_INPUT_EVENT_ID == 0.
+ // Therefore, we can skip setting the value for InputEventId here. If the value for
+ // INVALID_INPUT_EVENT_ID changes, this code would have to be updated, as well.
set(FrameInfoIndex::FrameDeadline) = std::numeric_limits<int64_t>::max();
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index af7271e96cb9..61f9960c4d8d 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -176,9 +176,6 @@ void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
paint.setBlendMode(SkBlendMode::kDstOut);
}
-
- // disabling AA on bitmap draws matches legacy HWUI behavior
- paint.setAntiAlias(false);
}
static SkFilterMode Paint_to_filter(const SkPaint& paint) {
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
index cee3635a1e3b..8186fb741b59 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
@@ -32,8 +32,8 @@ parcelable SoundModel {
* Unique vendor ID. Identifies the engine the sound model
* was build for */
String vendorUuid;
- /** Opaque data transparent to Android framework */
- ParcelFileDescriptor data;
+ /** Opaque data transparent to Android framework. May be null if dataSize is 0. */
+ @nullable ParcelFileDescriptor data;
/** Size of the above data, in bytes. */
int dataSize;
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 56f6c45bb50e..53f6fe24556b 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -445,8 +445,7 @@ static bool throwExceptionAsNecessary(
jniThrowException(env, "android/media/DeniedByServerException", msg);
return true;
} else if (err == DEAD_OBJECT) {
- jniThrowException(env, "android/media/MediaDrmResetException",
- "mediaserver died");
+ jniThrowException(env, "android/media/MediaDrmResetException", msg);
return true;
} else if (isSessionException(err)) {
throwSessionException(env, msg, err);
@@ -967,10 +966,12 @@ static void android_media_MediaDrm_native_setup(
status_t err = drm->initCheck();
if (err != OK) {
+ auto logs(DrmUtils::gLogBuf.getLogs());
+ auto msg(DrmUtils::GetExceptionMessage(err, "Failed to instantiate drm object", logs));
jniThrowException(
env,
"android/media/UnsupportedSchemeException",
- "Failed to instantiate drm object.");
+ msg.c_str());
return;
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 373fa3c24027..f5972fa34042 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -2,7 +2,7 @@
package android.net {
public class CaptivePortal implements android.os.Parcelable {
- method public void logEvent(int, @NonNull String);
+ method @Deprecated public void logEvent(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
method public void useNetwork();
field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortal.java b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
index 269bbf20c8b1..4a7b6016427b 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortal.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
@@ -160,12 +160,11 @@ public class CaptivePortal implements Parcelable {
* @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
* @param packageName captive portal application package name.
* @hide
+ * @deprecated The event will not be logged in Android S and above. The
+ * caller is migrating to statsd.
*/
+ @Deprecated
@SystemApi
public void logEvent(int eventId, @NonNull String packageName) {
- try {
- ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
- } catch (RemoteException e) {
- }
}
}
diff --git a/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
index fe21905c7002..e35f8d46afe7 100644
--- a/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
+++ b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
@@ -23,5 +23,4 @@ package android.net;
oneway interface ICaptivePortal {
void appRequest(int request);
void appResponse(int response);
- void logEvent(int eventId, String packageName);
}
diff --git a/packages/LocalTransport/OWNERS b/packages/LocalTransport/OWNERS
new file mode 100644
index 000000000000..d99779e3d9da
--- /dev/null
+++ b/packages/LocalTransport/OWNERS
@@ -0,0 +1 @@
+include /services/backup/OWNERS
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 139c8e59a148..63edc776b3cb 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -165,6 +165,9 @@ public class LocalTransport extends BackupTransport {
if (mParameters.isDeviceTransfer()) {
flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER;
}
+ if (mParameters.isEncrypted()) {
+ flags |= BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+ }
return flags;
}
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 2946db3cdcf0..1ba1bc6bfec7 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -28,10 +28,12 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
+ private static final String KEY_IS_ENCRYPTED = "is_encrypted";
private boolean mFakeEncryptionFlag;
private boolean mIsNonIncrementalOnly;
private boolean mIsDeviceTransfer;
+ private boolean mIsEncrypted;
public LocalTransportParameters(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -49,6 +51,10 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
return mIsDeviceTransfer;
}
+ boolean isEncrypted() {
+ return mIsEncrypted;
+ }
+
public String getSettingValue(ContentResolver resolver) {
return Settings.Secure.getString(resolver, SETTING);
}
@@ -57,5 +63,6 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
+ mIsEncrypted = parser.getBoolean(KEY_IS_ENCRYPTED, false);
}
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
new file mode 100644
index 000000000000..c799b9962828
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- The main content view -->
+<LinearLayout
+ android:id="@+id/content_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ android:transitionGroup="true"
+ android:orientation="vertical">
+ <Toolbar
+ android:id="@+id/action_bar"
+ style="?android:attr/actionBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/actionBarTheme" />
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 637805fc81c3..ad94cd0318a7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -24,6 +24,7 @@ import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
import androidx.fragment.app.FragmentActivity;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -40,8 +41,15 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- super.setContentView(R.layout.collapsing_toolbar_base_layout);
- mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+ // TODO(b/181723278): Update the version check after SDK for S is finalized
+ // The collapsing toolbar is only supported if the android platform version is S or higher.
+ // Otherwise the regular action bar will be shown.
+ if (BuildCompat.isAtLeastS()) {
+ super.setContentView(R.layout.collapsing_toolbar_base_layout);
+ mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+ } else {
+ super.setContentView(R.layout.toolbar_base_layout);
+ }
final Toolbar toolbar = findViewById(R.id.action_bar);
setActionBar(toolbar);
@@ -90,6 +98,14 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
super.setTitle(titleId);
}
+ @Override
+ public boolean onNavigateUp() {
+ if (!super.onNavigateUp()) {
+ finish();
+ }
+ return true;
+ }
+
/**
* Returns an instance of collapsing toolbar.
*/
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 79fbcc376b3c..96241194402a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1059,7 +1059,14 @@
<!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
<string name="accessibility_display_daltonizer_preference_title">Color correction</string>
<!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
- <string name="accessibility_display_daltonizer_preference_subtitle"><![CDATA[Color correction allows you to adjust how colors are displayed on your device]]></string>
+ <string name="accessibility_display_daltonizer_preference_subtitle">
+ <![CDATA[
+ Adjust how colors display on your device. This can be helpful when you want to:<br/><br/>
+ <ol>
+ <li> See colors more accurately</li>
+ <li> Remove colors to help you focus</li>
+ </ol>
+ ]]></string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
@@ -1295,6 +1302,8 @@
<!-- Name of the phone device. [CHAR LIMIT=30] -->
<string name="media_transfer_this_device_name">Phone speaker</string>
+ <!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
+ <string name="media_transfer_this_phone">This phone</string>
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index 228de039fc1b..35499c9b449a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -80,7 +80,8 @@ public class RecentLocationAccesses {
* Fills a list of applications which queried location recently within specified time.
* Apps are sorted by recency. Apps with more recent location accesses are in the front.
*/
- public List<Access> getAppList() {
+ @VisibleForTesting
+ List<Access> getAppList(boolean showSystemApps) {
// Retrieve a location usage list from AppOps
PackageManager pm = mContext.getPackageManager();
AppOpsManager aoManager =
@@ -108,22 +109,26 @@ public class RecentLocationAccesses {
// Don't show apps that do not have user sensitive location permissions
boolean showApp = true;
- for (int op : LOCATION_OPS) {
- final String permission = AppOpsManager.opToPermission(op);
- final int permissionFlags = pm.getPermissionFlags(permission, packageName, user);
- if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
- PermissionChecker.PID_UNKNOWN, uid, packageName)
- == PermissionChecker.PERMISSION_GRANTED) {
- if ((permissionFlags
- & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) == 0) {
- showApp = false;
- break;
- }
- } else {
- if ((permissionFlags
- & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0) {
- showApp = false;
- break;
+ if (!showSystemApps) {
+ for (int op : LOCATION_OPS) {
+ final String permission = AppOpsManager.opToPermission(op);
+ final int permissionFlags = pm.getPermissionFlags(permission, packageName,
+ user);
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
+ == 0) {
+ showApp = false;
+ break;
+ }
+ } else {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0) {
+ showApp = false;
+ break;
+ }
}
}
}
@@ -137,8 +142,15 @@ public class RecentLocationAccesses {
return accesses;
}
- public List<Access> getAppListSorted() {
- List<Access> accesses = getAppList();
+
+ /**
+ * Gets a list of apps that accessed location recently, sorting by recency.
+ *
+ * @param showSystemApps whether includes system apps in the list.
+ * @return the list of apps that recently accessed location.
+ */
+ public List<Access> getAppListSorted(boolean showSystemApps) {
+ List<Access> accesses = getAppList(showSystemApps);
// Sort the list of Access by recency. Most recent accesses first.
Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
index 245b7843110b..16d73a39d551 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -86,7 +86,7 @@ public class RecentLocationAccessesTest {
@Test
@Ignore
public void testGetAppList_shouldFilterRecentAccesses() {
- List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList();
+ List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(false);
// Only two of the apps have requested location within 15 min.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -115,7 +115,7 @@ public class RecentLocationAccessesTest {
mockTestApplicationInfos(
Process.SYSTEM_UID, RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME);
- List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList();
+ List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(true);
// Android OS shouldn't show up in the list of apps.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
new file mode 100644
index 000000000000..9b5752d2de59
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Enrollment progress bar-->
+ <com.android.systemui.biometrics.UdfpsProgressBar
+ android:id="@+id/progress_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:max="100"
+ android:padding="@dimen/udfps_enroll_progress_thickness"
+ android:progress="0"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view.xml b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
index 380dd855ffe1..f32faa0df867 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view.xml
+++ b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
@@ -14,8 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationView
+<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent">
+</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
new file mode 100644
index 000000000000..644d1adac46b
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 6ae306e17209..e24c9e99a405 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,15 +22,10 @@
android:layout_height="match_parent"
systemui:sensorTouchAreaCoefficient="0.5">
- <!-- Enrollment progress bar-->
- <com.android.systemui.biometrics.UdfpsProgressBar
- android:id="@+id/progress_bar"
+ <com.android.systemui.biometrics.UdfpsSurfaceView
+ android:id="@+id/hbm_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:max="100"
- android:padding="@dimen/udfps_enroll_progress_thickness"
- android:progress="0"
- android:layout_gravity="center"
- android:visibility="gone"/>
+ android:visibility="invisible"/>
</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4bdc16b050a8..b75a0bc1525a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -412,6 +412,9 @@
<!-- Whether or not child notifications that are part of a group will have shadows. -->
<bool name="config_enableShadowOnChildNotifications">true</bool>
+ <!-- If true, group numbers are shown in the expander instead of via "+N" overflow number -->
+ <bool name="config_showNotificationGroupCountInExpander">true</bool>
+
<!-- Whether or not a view containing child notifications will have a custom background when
it has been expanded to reveal its children. -->
<bool name="config_showGroupNotificationBgWhenExpanded">false</bool>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 2373d75cd4ea..83c2d1e7f684 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -405,14 +405,19 @@ public class KeyguardSliceView extends LinearLayout {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
public void setDarkAmount(float darkAmount) {
- boolean isAwake = darkAmount != 0;
- boolean wasAwake = mDarkAmount != 0;
- if (isAwake == wasAwake) {
+ boolean isDozing = darkAmount != 0;
+ boolean wasDozing = mDarkAmount != 0;
+ if (isDozing == wasDozing) {
return;
}
mDarkAmount = darkAmount;
- setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener);
+ setLayoutAnimationListener(isDozing ? null : mKeepAwakeListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index ed4d47cfa592..a02900328ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -31,11 +31,13 @@ import com.android.systemui.R;
* sensor area.
*/
public abstract class UdfpsAnimation extends Drawable {
- abstract void updateColor();
+ protected abstract void updateColor();
+ protected abstract void onDestroy();
@NonNull protected final Context mContext;
@NonNull protected final Drawable mFingerprintDrawable;
@Nullable private View mView;
+ private boolean mIlluminationShowing;
public UdfpsAnimation(@NonNull Context context) {
mContext = context;
@@ -61,6 +63,14 @@ public abstract class UdfpsAnimation extends Drawable {
mView = view;
}
+ boolean isIlluminationShowing() {
+ return mIlluminationShowing;
+ }
+
+ void setIlluminationShowing(boolean showing) {
+ mIlluminationShowing = showing;
+ }
+
/**
* @return The amount of padding that's needed on each side of the sensor, in pixels.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 5290986b2a1c..28b57195c5d4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -58,11 +58,16 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
}
@Override
- void updateColor() {
+ protected void updateColor() {
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
}
@Override
+ protected void onDestroy() {
+
+ }
+
+ @Override
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
super.onSensorRectUpdated(sensorRect);
mSensorRect = sensorRect;
@@ -70,6 +75,10 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
if (!isNightMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
index efc864ade5ff..ef7a34000841 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
@@ -34,12 +34,21 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation {
}
@Override
- void updateColor() {
+ protected void updateColor() {
+
+ }
+
+ @Override
+ protected void onDestroy() {
}
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
mFingerprintDrawable.draw(canvas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
index 8664e44c9ad2..5f268cfa8fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
@@ -42,6 +42,7 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
private static final String TAG = "UdfpsAnimationKeyguard";
@NonNull private final Context mContext;
+ @NonNull private final StatusBarStateController mStatusBarStateController;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
@@ -54,6 +55,7 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
@NonNull StatusBarStateController statusBarStateController) {
super(context);
mContext = context;
+ mStatusBarStateController = statusBarStateController;
mMaxBurnInOffsetX = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
@@ -89,6 +91,10 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
canvas.save();
canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
mFingerprintDrawable.draw(canvas);
@@ -106,11 +112,16 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
}
@Override
- public void updateColor() {
+ protected void updateColor() {
final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
final int ambientDisplayIconColor = Color.WHITE;
mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
ambientDisplayIconColor, mInterpolatedDarkAmount));
}
+
+ @Override
+ protected void onDestroy() {
+ mStatusBarStateController.removeCallback(this);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 44122cba8716..f4dd181eb329 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -22,41 +22,49 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.View;
+import android.widget.FrameLayout;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.phone.StatusBar;
/**
- * Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
- * FingerprintManager).
+ * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
+ * can support multiple child views drawing on the same region around the sensor location.
*/
-public class UdfpsAnimationView extends View implements DozeReceiver,
+public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
StatusBar.ExpansionChangedListener {
private static final String TAG = "UdfpsAnimationView";
+ @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
+
@NonNull private UdfpsView mParent;
- @Nullable private UdfpsAnimation mUdfpsAnimation;
@NonNull private RectF mSensorRect;
private int mAlpha;
public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
+ setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mUdfpsAnimation != null) {
+ if (getUdfpsAnimation() != null) {
final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
- mUdfpsAnimation.setAlpha(alpha);
- mUdfpsAnimation.draw(canvas);
+ getUdfpsAnimation().setAlpha(alpha);
+ getUdfpsAnimation().draw(canvas);
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getUdfpsAnimation().onDestroy();
+ }
+
private int expansionToAlpha(float expansion) {
// Fade to 0 opacity when reaching this expansion amount
final float maxExpansion = 0.4f;
@@ -69,38 +77,38 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
return (int) ((1 - percent) * 255);
}
- void setParent(@NonNull UdfpsView parent) {
- mParent = parent;
+ void onIlluminationStarting() {
+ getUdfpsAnimation().setIlluminationShowing(true);
+ postInvalidate();
}
- void setAnimation(@Nullable UdfpsAnimation animation) {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(null);
- }
+ void onIlluminationStopped() {
+ getUdfpsAnimation().setIlluminationShowing(false);
+ postInvalidate();
+ }
- mUdfpsAnimation = animation;
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(this);
- }
+ void setParent(@NonNull UdfpsView parent) {
+ mParent = parent;
}
void onSensorRectUpdated(@NonNull RectF sensorRect) {
mSensorRect = sensorRect;
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.onSensorRectUpdated(mSensorRect);
+ if (getUdfpsAnimation() != null) {
+ getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
}
}
void updateColor() {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.updateColor();
+ if (getUdfpsAnimation() != null) {
+ getUdfpsAnimation().updateColor();
}
+ postInvalidate();
}
@Override
public void dozeTimeTick() {
- if (mUdfpsAnimation instanceof DozeReceiver) {
- ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
+ if (getUdfpsAnimation() instanceof DozeReceiver) {
+ ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
}
}
@@ -111,16 +119,16 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
}
public int getPaddingX() {
- if (mUdfpsAnimation == null) {
+ if (getUdfpsAnimation() == null) {
return 0;
}
- return mUdfpsAnimation.getPaddingX();
+ return getUdfpsAnimation().getPaddingX();
}
public int getPaddingY() {
- if (mUdfpsAnimation == null) {
+ if (getUdfpsAnimation() == null) {
return 0;
}
- return mUdfpsAnimation.getPaddingY();
+ return getUdfpsAnimation().getPaddingY();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
new file mode 100644
index 000000000000..19e774937e20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
+ implements UdfpsEnrollHelper.Listener {
+
+ private static final String TAG = "UdfpsAnimationViewEnroll";
+
+ @NonNull private UdfpsAnimation mUdfpsAnimation;
+ @NonNull private UdfpsProgressBar mProgressBar;
+ @Nullable private UdfpsEnrollHelper mEnrollHelper;
+
+ @NonNull
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mUdfpsAnimation;
+ }
+
+ public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mUdfpsAnimation = new UdfpsAnimationEnroll(context);
+ }
+
+ public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+ mEnrollHelper = helper;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mProgressBar = findViewById(R.id.progress_bar);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "Enroll helper is null");
+ return;
+ }
+
+ if (mEnrollHelper.shouldShowProgressBar()) {
+ mProgressBar.setVisibility(View.VISIBLE);
+
+ // Only need enrollment updates if the progress bar is showing :)
+ mEnrollHelper.setListener(this);
+ }
+ }
+
+ @Override
+ public void onEnrollmentProgress(int remaining, int totalSteps) {
+ final int interpolatedProgress = mProgressBar.getMax()
+ * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+ mProgressBar.setProgress(interpolatedProgress, true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
new file mode 100644
index 000000000000..3d2f5a0fe5cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Class that coordinates non-HBM animations during other usage of FingerprintManager (not
+ * including Keyguard).
+ */
+public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
+
+ private final UdfpsAnimationFpmOther mAnimation;
+
+ public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mAnimation = new UdfpsAnimationFpmOther(context);
+ }
+
+ @Nullable
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mAnimation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
new file mode 100644
index 000000000000..7d0b3e59feb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
+ @Nullable private UdfpsAnimationKeyguard mAnimation;
+
+ public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
+ if (mAnimation == null) {
+ mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
+ mAnimation.setAnimationView(this);
+ }
+ }
+
+ @Nullable
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mAnimation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 71fba3302abc..e1d7eb3cbb19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -71,7 +71,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
- private final StatusBarStateController mStatusBarStateController;
+ @NonNull private final StatusBar mStatusBar;
+ @NonNull private final StatusBarStateController mStatusBarStateController;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -110,18 +111,20 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@Override
public void onEnrollmentProgress(int sensorId, int remaining) {
- if (mView == null) {
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "onEnrollProgress received but helper is null");
return;
}
- mView.onEnrollmentProgress(remaining);
+ mEnrollHelper.onEnrollmentProgress(remaining);
}
@Override
public void onEnrollmentHelp(int sensorId) {
- if (mView == null) {
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "onEnrollmentHelp received but helper is null");
return;
}
- mView.onEnrollmentHelp();
+ mEnrollHelper.onEnrollmentHelp();
}
@Override
@@ -135,20 +138,14 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@VisibleForTesting
final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
- (expansion, expanded) -> {
- if (mView != null) {
- mView.onExpansionChanged(expansion, expanded);
- }
- };
+ (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
@VisibleForTesting
final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
- if (mView != null) {
mView.onStateChanged(newState);
- }
}
};
@@ -189,7 +186,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @Nullable StatusBar statusBar) {
+ @NonNull StatusBar statusBar) {
mContext = context;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -197,6 +194,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
+ mStatusBar = statusBar;
mStatusBarStateController = statusBarStateController;
mSensorProps = findFirstUdfps();
@@ -217,9 +215,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
- statusBar.addExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
-
mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
}
@@ -278,7 +273,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
}
- private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimation animation) {
+ private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) {
final int paddingX = animation != null ? animation.getPaddingX() : 0;
final int paddingY = animation != null ? animation.getPaddingY() : 0;
@@ -330,13 +325,19 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
if (mView == null) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
-
+ // TODO: Eventually we should refactor the code to inflate an
+ // operation-specific view here, instead of inflating a generic udfps_view
+ // and adding operation-specific animations to it.
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
mView.setHbmCallback(this);
- final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
- mView.setExtras(animation, mEnrollHelper);
+ final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason);
+ mView.setAnimationView(animation);
+
+ mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
} catch (RuntimeException e) {
@@ -348,17 +349,34 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
});
}
- @Nullable
- private UdfpsAnimation getUdfpsAnimationForReason(int reason) {
+ @NonNull
+ private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
+
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+
switch (reason) {
case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
- return new UdfpsAnimationEnroll(mContext);
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
- return new UdfpsAnimationKeyguard(mContext, mStatusBarStateController);
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
- return new UdfpsAnimationFpmOther(mContext);
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
+ final UdfpsAnimationViewEnroll animation = (UdfpsAnimationViewEnroll)
+ inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
+ animation.setEnrollHelper(mEnrollHelper);
+ return animation;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
+ final UdfpsAnimationViewKeyguard animation = (UdfpsAnimationViewKeyguard)
+ inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
+ animation.setStatusBarStateController(mStatusBarStateController);
+ return animation;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
+ final UdfpsAnimationViewFpmOther animation = (UdfpsAnimationViewFpmOther)
+ inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
+ return animation;
+ }
+
default:
Log.d(TAG, "Animation for reason " + reason + " not supported yet");
return null;
@@ -371,6 +389,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "hideUdfpsOverlay | removing window");
// Reset the controller back to its starting state.
onFingerUp();
+
+ mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+
mWindowManager.removeView(mView);
mView = null;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 2442633a4a69..942fa7aae0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -16,22 +16,28 @@
package com.android.systemui.biometrics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import androidx.annotation.NonNull;
-
/**
* Helps keep track of enrollment state and animates the progress bar accordingly.
*/
public class UdfpsEnrollHelper {
private static final String TAG = "UdfpsEnrollHelper";
+ interface Listener {
+ void onEnrollmentProgress(int remaining, int totalSteps);
+ }
+
// IUdfpsOverlayController reason
private final int mEnrollReason;
private int mTotalSteps = -1;
private int mCurrentProgress = 0;
+ @Nullable Listener mListener;
+
public UdfpsEnrollHelper(int reason) {
mEnrollReason = reason;
}
@@ -40,21 +46,29 @@ public class UdfpsEnrollHelper {
return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
}
- void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+ void onEnrollmentProgress(int remaining) {
if (mTotalSteps == -1) {
mTotalSteps = remaining;
}
- mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
- / (mTotalSteps + 1);
- progressBar.setProgress(mCurrentProgress, true /* animate */);
+ if (mListener != null) {
+ mListener.onEnrollmentProgress(remaining, mTotalSteps);
+ }
}
- void updateProgress(@NonNull UdfpsProgressBar progressBar) {
- progressBar.setProgress(mCurrentProgress);
+ void onEnrollmentHelp() {
+
}
- void onEnrollmentHelp() {
+ void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ // Only notify during setListener if enrollment is already in progress, so the progress
+ // bar can be updated. If enrollment has not started yet, the progress bar will be empty
+ // anyway.
+ if (mTotalSteps != -1) {
+ final int remainingSteps = mTotalSteps - mCurrentProgress;
+ mListener.onEnrollmentProgress(remainingSteps, mTotalSteps);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 97c215e1d75f..61ec127ee946 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -52,6 +52,12 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
+ // Make this SurfaceView draw on top of everything else in this window. This allows us to
+ // 1) Always show the HBM circle on top of everything else, and
+ // 2) Properly composite this view with any other animations in the same window no matter
+ // what contents are added in which order to this view hierarchy.
+ setZOrderOnTop(true);
+
mHolder = getHolder();
mHolder.setFormat(PixelFormat.RGBA_8888);
@@ -61,7 +67,9 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
mSensorPaint.setARGB(255, 255, 255, 255);
mSensorPaint.setStyle(Paint.Style.FILL);
- mIlluminationDotDrawable = canvas -> canvas.drawOval(mSensorRect, mSensorPaint);
+ mIlluminationDotDrawable = canvas -> {
+ canvas.drawOval(mSensorRect, mSensorPaint);
+ };
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 399794391700..cd849e63ba9c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -32,7 +32,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -51,12 +50,11 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private static final int DEBUG_TEXT_SIZE_PX = 32;
- @NonNull private final UdfpsSurfaceView mHbmSurfaceView;
- @NonNull private final UdfpsAnimationView mAnimationView;
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
- @Nullable private UdfpsProgressBar mProgressBar;
+ @NonNull private UdfpsSurfaceView mHbmSurfaceView;
+ @Nullable private UdfpsAnimationView mAnimationView;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -66,7 +64,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
- @Nullable private UdfpsEnrollHelper mEnrollHelper;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -84,19 +81,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
a.recycle();
}
- // Inflate UdfpsSurfaceView
- final LayoutInflater inflater = LayoutInflater.from(context);
- mHbmSurfaceView = (UdfpsSurfaceView) inflater.inflate(R.layout.udfps_surface_view,
- null, false);
- addView(mHbmSurfaceView);
- mHbmSurfaceView.setVisibility(View.INVISIBLE);
-
- // Inflate UdfpsAnimationView
- mAnimationView = (UdfpsAnimationView) inflater.inflate(R.layout.udfps_animation_view,
- null, false);
- mAnimationView.setParent(this);
- addView(mAnimationView);
-
mSensorRect = new RectF();
mDebugTextPaint = new Paint();
@@ -107,22 +91,22 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
mIlluminationRequested = false;
}
+ @Override
+ protected void onFinishInflate() {
+ mHbmSurfaceView = findViewById(R.id.hbm_view);
+ }
+
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
mSensorProps = properties;
}
- void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
- mAnimationView.setAnimation(animation);
-
- mEnrollHelper = enrollHelper;
+ void setAnimationView(@NonNull UdfpsAnimationView animation) {
+ mAnimationView = animation;
+ animation.setParent(this);
- if (enrollHelper != null) {
- mEnrollHelper.updateProgress(mProgressBar);
- mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar()
- ? View.VISIBLE : View.GONE);
- } else {
- mProgressBar.setVisibility(View.GONE);
- }
+ // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
+ // after the animation type has been decided.
+ addView(animation, 0);
}
@Override
@@ -132,6 +116,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void dozeTimeTick() {
+ if (mAnimationView == null) {
+ return;
+ }
mAnimationView.dozeTimeTick();
}
@@ -143,12 +130,10 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void onExpansionChanged(float expansion, boolean expanded) {
mNotificationShadeExpanded = expanded;
- mAnimationView.onExpansionChanged(expansion, expanded);
- }
- @Override
- protected void onFinishInflate() {
- mProgressBar = findViewById(R.id.progress_bar);
+ if (mAnimationView != null) {
+ mAnimationView.onExpansionChanged(expansion, expanded);
+ }
}
@Override
@@ -229,7 +214,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
mIlluminationRequested = true;
- mAnimationView.setVisibility(View.INVISIBLE);
+ mAnimationView.onIlluminationStarting();
mHbmSurfaceView.setVisibility(View.VISIBLE);
mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
@@ -237,16 +222,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void stopIllumination() {
mIlluminationRequested = false;
- mAnimationView.setVisibility(View.VISIBLE);
+ mAnimationView.onIlluminationStopped();
mHbmSurfaceView.setVisibility(View.INVISIBLE);
mHbmSurfaceView.stopIllumination();
}
-
- void onEnrollmentProgress(int remaining) {
- mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
- }
-
- void onEnrollmentHelp() {
-
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 805ac7cf1ec9..3d6dea3cd3f0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -519,7 +519,7 @@ public class ScreenshotController {
setWindowFocusable(true);
if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
+ SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, true)) {
View decorView = mWindow.getDecorView();
// Wait until this window is attached to request because it is
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 6cdf6ab5154e..58a54f6ce0ed 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -82,7 +82,7 @@ public class ScreenshotNotificationsController {
dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
if (intent != null) {
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED, null, UserHandle.CURRENT);
+ mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
b.setContentIntent(pendingIntent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index bf65132166b6..9da6b8f240e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -38,13 +38,15 @@ public class ScrollCaptureController {
private static final float MAX_PAGES_DEFAULT = 3f;
private static final String SETTING_KEY_MAX_PAGES = "screenshot.scroll_max_pages";
+ // Portion of the tiles to be acquired above the starting position in infinite scroll
+ // situations. 1.0 means maximize the area above, 0 means just go down.
+ private static final float IDEAL_PORTION_ABOVE = 0.4f;
- private static final int UP = -1;
- private static final int DOWN = 1;
+ private boolean mScrollingUp = true;
+ // If true, stop acquiring images when no more bitmap data is available in the current direction
+ // or if the desired bitmap size is reached.
+ private boolean mFinishOnBoundary;
- private int mDirection = DOWN;
- private boolean mAtBottomEdge;
- private boolean mAtTopEdge;
private Session mSession;
public static final int MAX_HEIGHT = 12000;
@@ -86,7 +88,8 @@ public class ScrollCaptureController {
}
private void onCaptureResult(CaptureResult result) {
- Log.d(TAG, "onCaptureResult: " + result);
+ Log.d(TAG, "onCaptureResult: " + result + " scrolling up: " + mScrollingUp
+ + " finish on boundary: " + mFinishOnBoundary);
boolean emptyResult = result.captured.height() == 0;
boolean partialResult = !emptyResult
&& result.captured.height() < result.requested.height();
@@ -94,34 +97,28 @@ public class ScrollCaptureController {
if (partialResult || emptyResult) {
// Potentially reached a vertical boundary. Extend in the other direction.
- switch (mDirection) {
- case DOWN:
- Log.d(TAG, "Reached bottom edge.");
- mAtBottomEdge = true;
- mDirection = UP;
- break;
- case UP:
- Log.d(TAG, "Reached top edge.");
- mAtTopEdge = true;
- mDirection = DOWN;
- break;
+ if (mFinishOnBoundary) {
+ finish = true;
+ } else {
+ // We hit a boundary, clear the tiles, capture everything in the opposite direction,
+ // then finish.
+ mImageTileSet.clear();
+ mFinishOnBoundary = true;
+ mScrollingUp = !mScrollingUp;
}
-
- if (mAtTopEdge && mAtBottomEdge) {
- Log.d(TAG, "Reached both top and bottom edge, ending.");
+ } else {
+ // Got the full requested result, but may have got enough bitmap data now
+ int expectedTiles = mImageTileSet.size() + 1;
+ boolean hitMaxTiles = expectedTiles >= mSession.getMaxTiles();
+ if (hitMaxTiles && mFinishOnBoundary) {
finish = true;
} else {
- // only reverse if the edge was relatively close to the starting point
- if (mImageTileSet.getHeight() < mSession.getPageHeight() * 3) {
- Log.d(TAG, "Restarting in reverse direction.");
-
- // Because of temporary limitations, we cannot just jump to the opposite edge
- // and continue there. Instead, clear the results and start over capturing from
- // here in the other direction.
- mImageTileSet.clear();
- } else {
- Log.d(TAG, "Capture is tall enough, stopping here.");
- finish = true;
+ if (mScrollingUp) {
+ if (expectedTiles >= mSession.getMaxTiles() * IDEAL_PORTION_ABOVE) {
+ // We got enough above the start point, now see how far down it can go.
+ mImageTileSet.clear();
+ mScrollingUp = false;
+ }
}
}
}
@@ -136,9 +133,8 @@ public class ScrollCaptureController {
// Stop when "too tall"
- if (mImageTileSet.size() >= mSession.getMaxTiles()
- || mImageTileSet.getHeight() > MAX_HEIGHT) {
- Log.d(TAG, "Max height and/or tile count reached.");
+ if (mImageTileSet.getHeight() > MAX_HEIGHT) {
+ Log.d(TAG, "Max height reached.");
finish = true;
}
@@ -150,8 +146,8 @@ public class ScrollCaptureController {
return;
}
- int nextTop = (mDirection == DOWN) ? result.captured.bottom
- : result.captured.top - mSession.getTileHeight();
+ int nextTop = (mScrollingUp)
+ ? result.captured.top - mSession.getTileHeight() : result.captured.bottom;
Log.d(TAG, "requestTile: " + nextTop);
mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index 9ed9659c7ab8..f2adaf042b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -207,7 +207,7 @@ public class GestureRecorder {
sb.append(g.toJson());
count++;
}
- mLastSaveLen += count;
+ mLastSaveLen = count;
sb.append("]");
return sb.toString();
}
@@ -249,9 +249,7 @@ public class GestureRecorder {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
save();
if (mLastSaveLen >= 0) {
- pw.println(String.valueOf(mLastSaveLen)
- + " gestures since last dump written to " + mLogfile);
- mLastSaveLen = 0;
+ pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile);
} else {
pw.println("error writing gestures");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 01d31039a749..e5a960e13e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -83,6 +83,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
+ /** Maximum allowed width or height for an icon drawable */
+ private static final int MAX_IMAGE_SIZE = 500;
+
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
= new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -378,6 +381,13 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
return false;
}
+
+ if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
+ || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
+ Log.w(TAG, "Drawable is too large " + mIcon);
+ return false;
+ }
+
if (withClear) {
setImageDrawable(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1251b58171da..52063285ac01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2052,8 +2052,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationLaunchHeight,
zProgress);
setTranslationZ(translationZ);
- float extraWidthForClipping = params.getWidth() - getWidth()
- + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
+ float extraWidthForClipping = params.getWidth() - getWidth();
setExtraWidthForClipping(extraWidthForClipping);
int top = params.getTop();
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 76917612b910..b0b91bd5177c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -22,6 +22,8 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -30,6 +32,7 @@ import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
@@ -1234,6 +1237,7 @@ public class NotificationContentView extends FrameLayout {
mCachedHeadsUpRemoteInput = null;
}
+
private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
boolean hasRemoteInput, PendingIntent existingPendingIntent,
RemoteInputView cachedView, NotificationViewWrapper wrapper) {
@@ -1271,6 +1275,15 @@ public class NotificationContentView extends FrameLayout {
if (color == Notification.COLOR_DEFAULT) {
color = mContext.getColor(R.color.default_remote_input_background);
}
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_tintNotificationsWithTheme)) {
+ Resources.Theme theme = new ContextThemeWrapper(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(
+ new int[]{com.android.internal.R.attr.colorAccent});
+ color = ta.getColor(0, color);
+ ta.recycle();
+ }
existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
mContext.getColor(R.color.remote_input_text_enabled),
mContext.getColor(R.color.remote_input_hint)));
@@ -1342,12 +1355,10 @@ public class NotificationContentView extends FrameLayout {
&& isPersonWithShortcut
&& entry.getBubbleMetadata() != null;
if (showButton) {
- Drawable d = mContext.getResources().getDrawable(entry.isBubble()
+ // explicitly resolve drawable resource using SystemUI's theme
+ Drawable d = mContext.getDrawable(entry.isBubble()
? R.drawable.bubble_ic_stop_bubble
: R.drawable.bubble_ic_create_bubble);
- mContainingNotification.updateNotificationColor();
- final int tint = mContainingNotification.getNotificationColor();
- d.setTint(tint);
String contentDescription = mContext.getResources().getString(entry.isBubble()
? R.string.notification_conversation_unbubble
@@ -1381,9 +1392,8 @@ public class NotificationContentView extends FrameLayout {
return;
}
+ // explicitly resolve drawable resource using SystemUI's theme
Drawable snoozeDrawable = mContext.getDrawable(R.drawable.ic_snooze);
- mContainingNotification.updateNotificationColor();
- snoozeDrawable.setTint(mContainingNotification.getNotificationColor());
snoozeButton.setImageDrawable(snoozeDrawable);
final NotificationSnooze snoozeGuts = (NotificationSnooze) LayoutInflater.from(mContext)
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 756fe6c5ba24..8446b4e6a3f0 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
@@ -133,7 +133,7 @@ public class AmbientState {
*/
public static int getNotificationLaunchHeight(Context context) {
int zDistance = getZDistanceBetweenElements(context);
- return getBaseHeight(zDistance) * 2;
+ return NOTIFICATIONS_HAVE_SHADOWS ? 2 * getBaseHeight(zDistance) : 4 * zDistance;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3833637e8542..3739424b4f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -20,10 +20,12 @@ import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Pair;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -33,6 +35,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
@@ -103,6 +106,8 @@ public class NotificationChildrenContainer extends ViewGroup {
private ViewGroup mCurrentHeader;
private boolean mIsConversation;
+ private boolean mTintWithThemeAccent;
+ private boolean mShowGroupCountInExpander;
private boolean mShowDividersWhenExpanded;
private boolean mHideDividersDuringExpand;
private int mTranslationForHeader;
@@ -145,6 +150,10 @@ public class NotificationChildrenContainer extends ViewGroup {
com.android.internal.R.dimen.notification_content_margin);
mEnableShadowOnChildNotifications =
res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
+ mTintWithThemeAccent =
+ res.getBoolean(com.android.internal.R.bool.config_tintNotificationsWithTheme);
+ mShowGroupCountInExpander =
+ res.getBoolean(R.bool.config_showNotificationGroupCountInExpander);
mShowDividersWhenExpanded =
res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
mHideDividersDuringExpand =
@@ -229,7 +238,6 @@ public class NotificationChildrenContainer extends ViewGroup {
mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
}
if (mNotificationHeaderLowPriority != null) {
- headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
}
@@ -397,7 +405,20 @@ public class NotificationChildrenContainer extends ViewGroup {
mGroupingUtil.updateChildrenAppearance();
}
+ private void setExpandButtonNumber(NotificationViewWrapper wrapper) {
+ View expandButton = wrapper == null
+ ? null : wrapper.getExpandButton();
+ if (expandButton instanceof NotificationExpandButton) {
+ ((NotificationExpandButton) expandButton).setNumber(mUntruncatedChildCount);
+ }
+ }
+
public void updateGroupOverflow() {
+ if (mShowGroupCountInExpander) {
+ setExpandButtonNumber(mNotificationHeaderWrapper);
+ setExpandButtonNumber(mNotificationHeaderWrapperLowPriority);
+ return;
+ }
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
if (mUntruncatedChildCount > maxAllowedVisibleChildren) {
int number = mUntruncatedChildCount - maxAllowedVisibleChildren;
@@ -1201,8 +1222,21 @@ public class NotificationChildrenContainer extends ViewGroup {
}
public void onNotificationUpdated() {
- mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
- mContainingNotification.getNotificationColor());
+ if (mShowGroupCountInExpander) {
+ // The overflow number is not used, so its color is irrelevant; skip this
+ return;
+ }
+ int color = mContainingNotification.getNotificationColor();
+ if (mTintWithThemeAccent) {
+ // We're using the theme accent, color with the accent color instead of the notif color
+ Resources.Theme theme = new ContextThemeWrapper(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(
+ new int[]{com.android.internal.R.attr.colorAccent});
+ color = ta.getColor(0, color);
+ ta.recycle();
+ }
+ mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
}
public int getPositionInLinearLayout(View childInGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b25fced6a212..bf36435b78c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -78,7 +78,6 @@ import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -277,8 +276,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
- public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858)
- public static final boolean DEBUG_GESTURES_VERBOSE = true;
+ public static final boolean DEBUG_GESTURES = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
public static final boolean DEBUG_CAMERA_LIFT = false;
@@ -458,7 +456,9 @@ public class StatusBar extends SystemUI implements DemoMode,
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
- private GestureRecorder mGestureRec = null;
+ private final GestureRecorder mGestureRec = DEBUG_GESTURES
+ ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
+ : null;
private final ScreenPinningRequest mScreenPinningRequest;
@@ -856,10 +856,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityIntentHelper = new ActivityIntentHelper(mContext);
DateTimeView.setReceiverHandler(timeTickHandler);
-
- if (DEBUG_GESTURES) {
- mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat");
- }
}
@Override
@@ -2271,7 +2267,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public boolean interceptTouchEvent(MotionEvent event) {
if (DEBUG_GESTURES) {
- if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+ if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
event.getActionMasked(), (int) event.getX(), (int) event.getY(),
mDisabled1, mDisabled2);
@@ -2696,6 +2692,10 @@ public class StatusBar extends SystemUI implements DemoMode,
return mDisplay.getRotation();
}
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
boolean dismissShade, int flags) {
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
@@ -2721,7 +2721,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(flags);
int result = ActivityManager.START_CANCELED;
- ActivityOptions options = new ActivityOptions(getActivityOptions(
+ ActivityOptions options = new ActivityOptions(getActivityOptions(mDisplayId,
null /* remoteAnimation */));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
@@ -4366,6 +4366,7 @@ public class StatusBar extends SystemUI implements DemoMode,
executeActionDismissingKeyguard(() -> {
try {
intent.send(null, 0, null, null, null, null, getActivityOptions(
+ mDisplayId,
mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
@@ -4387,15 +4388,38 @@ public class StatusBar extends SystemUI implements DemoMode,
mMainThreadHandler.post(runnable);
}
- public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
+ /**
+ * Returns an ActivityOptions bundle created using the given parameters.
+ *
+ * @param displayId The ID of the display to launch the activity in. Typically this would be the
+ * display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
+ */
+ public static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter) {
return getDefaultActivityOptions(animationAdapter).toBundle();
}
- public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter,
- boolean isKeyguardShowing, long eventTime) {
+ /**
+ * Returns an ActivityOptions bundle created using the given parameters.
+ *
+ * @param displayId The ID of the display to launch the activity in. Typically this would be the
+ * display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
+ * @param isKeyguardShowing Whether keyguard is currently showing.
+ * @param eventTime The event time in milliseconds since boot, not including sleep. See
+ * {@link ActivityOptions#setSourceInfo}.
+ */
+ public static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter, boolean isKeyguardShowing,
+ long eventTime) {
ActivityOptions options = getDefaultActivityOptions(animationAdapter);
options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
: ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
+ options.setLaunchDisplayId(displayId);
+ options.setCallerDisplayId(displayId);
return options.toBundle();
}
@@ -4535,4 +4559,8 @@ public class StatusBar extends SystemUI implements DemoMode,
public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
mExpansionChangedListeners.add(listener);
}
+
+ public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
+ mExpansionChangedListeners.remove(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 598addc68d2e..34673f2503ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -427,8 +427,13 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
intent.getCreatorPackage(), adapter);
}
long eventTime = row.getAndResetLastActionUpTime();
- Bundle options = eventTime > 0 ? getActivityOptions(adapter,
- mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(adapter);
+ Bundle options = eventTime > 0
+ ? getActivityOptions(
+ mStatusBar.getDisplayId(),
+ adapter,
+ mKeyguardStateController.isShowing(),
+ eventTime)
+ : getActivityOptions(mStatusBar.getDisplayId(), adapter);
int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, options);
mMainThreadHandler.post(() -> {
@@ -450,6 +455,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
int launchResult = TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
+ mStatusBar.getDisplayId(),
mActivityLaunchAnimator.getLaunchAnimation(
row, mStatusBar.isOccluded())),
new UserHandle(UserHandle.getUserId(appUid)));
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 38f3bc891394..59c1138431fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -94,15 +94,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
goingToFullShade,
oldState);
}
-
- @Override
- public void onDozeAmountChanged(float linearAmount, float amount) {
- if (DEBUG) {
- Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
- linearAmount, amount));
- }
- setDarkAmount(amount);
- }
};
@Inject
@@ -294,20 +285,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
}
}
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- *
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- private void setDarkAmount(float darkAmount) {
- boolean isAwake = darkAmount != 0;
- if (darkAmount == mDarkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
- }
-
private boolean isListAnimating() {
return mKeyguardVisibilityHelper.isVisibilityAnimating();
}
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 8845a05cf6f5..5a80c05cc3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -205,7 +205,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
protected void onViewAttached() {
if (DEBUG) Log.d(TAG, "onViewAttached");
mAdapter.registerDataSetObserver(mDataSetObserver);
- mDataSetObserver.onChanged();
+ mAdapter.notifyDataSetChanged();
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mScreenLifecycle.addObserver(mScreenObserver);
@@ -373,14 +373,13 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
private void setDarkAmount(float darkAmount) {
- boolean isAwake = darkAmount != 0;
+ boolean isFullyDozed = darkAmount == 1;
if (darkAmount == mDarkAmount) {
return;
}
mDarkAmount = darkAmount;
mListView.setDarkAmount(darkAmount);
- mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
- if (!isAwake) {
+ if (isFullyDozed) {
closeSwitcherIfOpenAndNotSimple(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 738cab15431a..5638503be198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -439,7 +439,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
- if (DEBUG) Log.d(TAG, "onAvailable " + network.netId);
+ if (DEBUG) Log.d(TAG, "onAvailable " + network.getNetId());
updateState();
fireCallbacks();
};
@@ -448,7 +448,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
// how long the VPN connection is held on to.
@Override
public void onLost(Network network) {
- if (DEBUG) Log.d(TAG, "onLost " + network.netId);
+ if (DEBUG) Log.d(TAG, "onLost " + network.getNetId());
updateState();
fireCallbacks();
};
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 25345d5c4b4c..5dc7006406ee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -778,6 +778,12 @@ public class VolumeDialogImpl implements VolumeDialog,
/** Animates away the ringer drawer. */
private void hideRingerDrawer() {
+
+ // If the ringer drawer isn't present, don't try to hide it.
+ if (mRingerDrawerContainer == null) {
+ return;
+ }
+
// Hide the drawer icon for the selected ringer - it's visible in the ringer button and we
// don't want to be able to see it while it animates away.
getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 700f101e44b8..fb778e813adf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -242,9 +242,18 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
- public void registersViewForCallbacks() throws RemoteException {
+ public void registersAndUnregistersViewForCallbacks() throws RemoteException {
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+ IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
+ mFgExecutor.runAllReady();
verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
verify(mStatusBar).addExpansionChangedListener(
mUdfpsController.mStatusBarExpansionListener);
+
+ mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mFgExecutor.runAllReady();
+ verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener);
+ verify(mStatusBar).removeExpansionChangedListener(
+ mUdfpsController.mStatusBarExpansionListener);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 71f146bf0220..f31639cef666 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -35,6 +35,7 @@ import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -121,4 +122,13 @@ public class StatusBarIconViewTest extends SysuiTestCase {
assertEquals("Transparent backgrounds should fallback to drawable color",
color, mIconView.getStaticDrawableColor());
}
+
+ @Test
+ public void testGiantImageNotAllowed() {
+ Bitmap largeBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
+ Icon icon = Icon.createWithBitmap(largeBitmap);
+ StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ icon, 0, 0, "");
+ assertFalse(mIconView.set(largeIcon));
+ }
} \ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 065e2bbd3eef..88e6b66e23a3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -102,6 +102,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
+ private static final String TRACE_A11Y_SERVICE_CONNECTION =
+ LOG_TAG + ".IAccessibilityServiceConnection";
+ private static final String TRACE_A11Y_SERVICE_CLIENT =
+ LOG_TAG + ".IAccessibilityServiceClient";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
protected static final String TAKE_SCREENSHOT = "takeScreenshot";
@@ -127,6 +131,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected final Object mLock;
protected final AccessibilitySecurityPolicy mSecurityPolicy;
+ protected final AccessibilityTrace mTrace;
// The service that's bound to this instance. Whenever this value is non-null, this
// object is registered as a death recipient
@@ -247,7 +252,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
- WindowManagerInternal windowManagerInternal,
+ AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
mContext = context;
@@ -259,6 +264,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerfomer;
mSystemSupport = systemSupport;
+ mTrace = trace;
mMainHandler = mainHandler;
mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
mA11yWindowManager = a11yWindowManager;
@@ -291,6 +297,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent",
+ keyEvent + ", " + sequenceNumber);
+ }
mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
} catch (RemoteException e) {
return false;
@@ -354,11 +364,18 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult",
+ "handled=" + handled + ";sequence=" + sequence);
+ }
mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
public AccessibilityServiceInfo getServiceInfo() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo");
+ }
synchronized (mLock) {
return mAccessibilityServiceInfo;
}
@@ -375,6 +392,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info);
+ }
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -400,6 +420,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Nullable
@Override
public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows");
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
@@ -434,6 +457,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId);
+ }
synchronized (mLock) {
int displayId = Display.INVALID_DISPLAY;
if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
@@ -469,6 +495,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
+ }
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
@@ -530,6 +563,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
+ + ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
+ }
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
@@ -591,6 +630,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(
+ TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+ + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+ + ";arguments=" + arguments);
+ }
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
@@ -652,6 +699,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
+ }
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
@@ -713,6 +767,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";direction=" + direction + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
+ }
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
@@ -770,10 +831,18 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture",
+ "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
+ }
}
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence="
+ + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId);
+ }
}
@Override
@@ -781,6 +850,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction",
+ "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+ + ";interactionId=" + interactionId + ";callback=" + callback
+ + ";interrogatingTid=" + interrogatingTid);
+ }
final int resolvedWindowId;
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -802,6 +878,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean performGlobalAction(int action) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction",
+ "action=" + action);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -812,6 +892,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions");
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return Collections.emptyList();
@@ -822,6 +905,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean isFingerprintGestureDetectionAvailable() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(
+ TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable");
+ }
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return false;
}
@@ -835,6 +922,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationScale(int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale",
+ "displayId=" + displayId);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 1.0f;
@@ -850,6 +941,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public Region getMagnificationRegion(int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion",
+ "displayId=" + displayId);
+ }
synchronized (mLock) {
final Region region = Region.obtain();
if (!hasRightsToCurrentUserLocked()) {
@@ -874,6 +969,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterX(int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX",
+ "displayId=" + displayId);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
@@ -896,6 +995,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterY(int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY",
+ "displayId=" + displayId);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
@@ -928,6 +1031,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean resetMagnification(int displayId, boolean animate) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification",
+ "displayId=" + displayId + ";animate=" + animate);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -950,6 +1057,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
float centerY, boolean animate) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter",
+ "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ + ";centerY=" + centerY + ";animate=" + animate);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -974,6 +1086,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled",
+ "displayId=" + displayId + ";enabled=" + enabled);
+ }
mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
}
@@ -983,11 +1099,19 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled",
+ "enabled=" + enabled);
+ }
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot",
+ "displayId=" + displayId + ";callback=" + callback);
+ }
final long currentTimestamp = SystemClock.uptimeMillis();
if (mRequestTakeScreenshotTimestampMs != 0
&& (currentTimestamp - mRequestTakeScreenshotTimestampMs)
@@ -1157,6 +1281,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public IBinder getOverlayWindowToken(int displayId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken",
+ "displayId=" + displayId);
+ }
synchronized (mLock) {
return mOverlayWindowTokens.get(displayId);
}
@@ -1170,6 +1298,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken",
+ "token=" + token);
+ }
synchronized (mLock) {
return mA11yWindowManager.getWindowIdLocked(token);
}
@@ -1181,6 +1313,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
if (mServiceInterface != null) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null");
+ }
mServiceInterface.init(null, mId, null);
}
} catch (RemoteException re) {
@@ -1329,6 +1464,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent",
+ event + ";" + serviceWantsEvent);
+ }
listener.onAccessibilityEvent(event, serviceWantsEvent);
if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
@@ -1382,6 +1521,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId
+ + ", " + region + ", " + scale + ", " + centerX + ", " + centerY);
+ }
listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
@@ -1397,6 +1540,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged",
+ String.valueOf(showState));
+ }
listener.onSoftKeyboardShowModeChanged(showState);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService,
@@ -1409,6 +1556,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked",
+ String.valueOf(displayId));
+ }
listener.onAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re);
@@ -1427,6 +1578,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(
+ TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged",
+ String.valueOf(available));
+ }
listener.onAccessibilityButtonAvailabilityChanged(available);
} catch (RemoteException re) {
Slog.e(LOG_TAG,
@@ -1440,6 +1596,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture",
+ gestureInfo.toString());
+ }
listener.onGesture(gestureInfo);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo
@@ -1452,6 +1612,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged");
+ }
listener.onSystemActionsChanged();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending system actions change to " + mService,
@@ -1464,6 +1627,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache");
+ }
listener.clearAccessibilityCache();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
@@ -1790,14 +1956,27 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion",
+ "displayId=" + displayId + ";region=" + region);
+ }
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
}
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion",
+ "displayId=" + displayId + ";region=" + region);
+ }
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
}
@Override
- public void setFocusAppearance(int strokeWidth, int color) { }
+ public void setFocusAppearance(int strokeWidth, int color) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance",
+ "strokeWidth=" + strokeWidth + ";color=" + color);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c63c2e1a257d..b3be0448edaf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -149,6 +149,7 @@ import java.util.function.Predicate;
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
+ AccessibilityTrace,
AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager,
@@ -243,6 +244,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
+ private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -288,6 +290,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+ mA11yController = mWindowManagerService.getAccessibilityController();
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
@@ -308,6 +311,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+ mA11yController = mWindowManagerService.getAccessibilityController();
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -328,16 +332,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public int getCurrentUserIdLocked() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getCurrentUserIdLocked");
+ }
return mCurrentUserId;
}
@Override
public boolean isAccessibilityButtonShown() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".isAccessibilityButtonShown");
+ }
return mIsAccessibilityButtonShown;
}
@Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
+ }
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
@@ -395,6 +408,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+ }
+
synchronized (mLock) {
// Only the profile parent can install accessibility services.
// Therefore we ignore packages from linked profiles.
@@ -419,6 +436,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// mBindingServices in binderDied() during updating. Remove services from this
// package from mBindingServices, and then update the user state to re-bind new
// versions of them.
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+ "packageName=" + packageName + ";uid=" + uid);
+ }
synchronized (mLock) {
final int userId = getChangingUserId();
if (userId != mCurrentUserId) {
@@ -448,6 +469,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onPackageRemoved(String packageName, int uid) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ "packageName=" + packageName + ";uid=" + uid);
+ }
+
synchronized (mLock) {
final int userId = getChangingUserId();
// Only the profile parent can install accessibility services.
@@ -487,6 +513,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".PM.onHandleForceStop", "intent=" + intent + ";packages="
+ + packages + ";uid=" + uid + ";doit=" + doit);
+ }
synchronized (mLock) {
final int userId = getChangingUserId();
// Only the profile parent can install accessibility services.
@@ -533,6 +563,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".BR.onReceive", "context=" + context + ";intent=" + intent);
+ }
+
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
@@ -616,6 +650,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".addClient", "callback=" + callback + ";userId=" + userId);
+ }
+
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -654,6 +692,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".sendAccessibilityEvent", "event=" + event + ";userId=" + userId);
+ }
boolean dispatchEvent = false;
synchronized (mLock) {
@@ -746,6 +787,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".registerSystemAction", "action=" + action + ";actionId="
+ + actionId);
+ }
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
}
@@ -757,6 +802,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void unregisterSystemAction(int actionId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+ }
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
}
@@ -771,6 +819,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList", "userId=" + userId);
+ }
+
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -788,6 +840,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+ "feedbackType=" + feedbackType + ";userId=" + userId);
+ }
+
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -816,6 +873,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void interrupt(int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+ }
+
List<IAccessibilityServiceClient> interfacesToInterrupt;
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
@@ -842,6 +903,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+ }
interfacesToInterrupt.get(i).onInterrupt();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending interrupt request to "
@@ -854,18 +918,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+ "windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
+ + connection + "; packageName=" + packageName + ";userId=" + userId);
+ }
+
return mA11yWindowManager.addAccessibilityInteractionConnection(
windowToken, leashToken, connection, packageName, userId);
}
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection", "window=" + window);
+ }
mA11yWindowManager.removeAccessibilityInteractionConnection(window);
}
@Override
public void setPictureInPictureActionReplacingConnection(
IAccessibilityInteractionConnection connection) throws RemoteException {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
+ "connection=" + connection);
+ }
mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
SET_PIP_ACTION_REPLACEMENT);
mA11yWindowManager.setPictureInPictureActionReplacingConnection(connection);
@@ -876,13 +953,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
int flags) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
+ + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
+ + accessibilityServiceInfo + ";flags=" + flags);
+ }
+
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
synchronized (mLock) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
- mSecurityPolicy, this, mWindowManagerService, getSystemActionPerformer(),
+ mSecurityPolicy, this, this, mWindowManagerService, getSystemActionPerformer(),
mA11yWindowManager, flags);
onUserStateChangedLocked(getCurrentUserStateLocked());
}
@@ -890,6 +973,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
+ "serviceClient=" + serviceClient);
+ }
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
}
@@ -898,6 +985,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+ "service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
+ }
+
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
@@ -926,6 +1018,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public IBinder getWindowToken(int windowId, int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getWindowToken", "windowId=" + windowId + ";userId=" + userId);
+ }
+
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.RETRIEVE_WINDOW_TOKEN,
GET_WINDOW_TOKEN);
@@ -965,6 +1061,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+ "displayId=" + displayId + ";targetName=" + targetName);
+ }
+
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
@@ -990,6 +1091,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged", "shown=" + shown);
+ }
+
mSecurityPolicy.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR_SERVICE);
synchronized (mLock) {
@@ -1018,6 +1123,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void onSystemActionsChanged() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".onSystemActionsChanged");
+ }
+
synchronized (mLock) {
AccessibilityUserState state = getCurrentUserStateLocked();
notifySystemActionsChangedLocked(state);
@@ -1080,6 +1189,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked", "displayId=" + displayId);
+ }
+
final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
MotionEventInjector motionEventInjector = null;
while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1646,6 +1759,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked", "settingName=" + settingName
+ + ";componentNames=" + componentNames + ";userId=" + userId);
+ }
+
persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
componentName -> componentName.flattenToShortString());
}
@@ -1730,7 +1848,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- this, mWindowManagerService, getSystemActionPerformer(),
+ this, this, mWindowManagerService, getSystemActionPerformer(),
mA11yWindowManager, mActivityTaskManagerService);
} else if (userState.mBoundServices.contains(service)) {
continue;
@@ -2607,6 +2725,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked", "windowId=" + windowId);
+ }
+
IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
mCurrentUserId, windowId);
if (windowToken != null) {
@@ -2618,6 +2740,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public KeyEventDispatcher getKeyEventDispatcher() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getKeyEventDispatcher");
+ }
+
if (mKeyEventDispatcher == null) {
mKeyEventDispatcher = new KeyEventDispatcher(
mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
@@ -2630,6 +2756,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getPendingIntentActivity", "context=" + context + ";requestCode="
+ + requestCode + ";intent=" + intent + ";flags=" + flags);
+ }
+
+
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
@@ -2644,6 +2776,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void performAccessibilityShortcut(String targetName) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".performAccessibilityShortcut", "targetName=" + targetName);
+ }
+
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
&& (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -2828,6 +2964,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getAccessibilityShortcutTargets", "shortcutType=" + shortcutType);
+ }
+
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
@@ -2897,6 +3037,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked", "event=" + event);
+ }
+
sendAccessibilityEventLocked(event, mCurrentUserId);
}
@@ -2918,6 +3062,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public boolean sendFingerprintGesture(int gestureKeyCode) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".sendFingerprintGesture", "gestureKeyCode=" + gestureKeyCode);
+ }
+
synchronized(mLock) {
if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
throw new SecurityException("Only SYSTEM can call sendFingerprintGesture");
@@ -2939,6 +3087,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getAccessibilityWindowId", "windowToken=" + windowToken);
+ }
+
synchronized (mLock) {
if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
throw new SecurityException("Only SYSTEM can call getAccessibilityWindowId");
@@ -2956,6 +3108,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public long getRecommendedTimeoutMillis() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+ }
+
synchronized(mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
return getRecommendedTimeoutMillisLocked(userState);
@@ -2970,6 +3126,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setWindowMagnificationConnection(
IWindowMagnificationConnection connection) throws RemoteException {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".setWindowMagnificationConnection", "connection=" + connection);
+ }
+
mSecurityPolicy.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR_SERVICE);
@@ -3000,6 +3160,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
+ "host=" + host + ";embedded=" + embedded);
+ }
+
synchronized (mLock) {
mA11yWindowManager.associateEmbeddedHierarchyLocked(host, embedded);
}
@@ -3007,6 +3172,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+ }
+
synchronized (mLock) {
mA11yWindowManager.disassociateEmbeddedHierarchyLocked(token);
}
@@ -3084,6 +3253,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".getFullScreenMagnificationController");
+ }
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
}
@@ -3091,6 +3263,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".onClientChangeLocked", "serviceInfoChanged=" + serviceInfoChanged);
+ }
+
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
@@ -3126,8 +3302,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityServiceConnection service = new AccessibilityServiceConnection(
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- AccessibilityManagerService.this, mWindowManagerService,
- getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService) {
+ AccessibilityManagerService.this, AccessibilityManagerService.this,
+ mWindowManagerService, getSystemActionPerformer(), mA11yWindowManager,
+ mActivityTaskManagerService) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
@@ -3614,6 +3791,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
+ "displayId=" + displayId + ";region=" + region);
+ }
+
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal,
@@ -3624,6 +3806,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
+ if (isA11yTracingEnabled()) {
+ logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
+ "displayId=" + displayId + ";region=" + region);
+ }
+
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal,
@@ -3661,4 +3848,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
});
}
+
+ @Override
+ public boolean isA11yTracingEnabled() {
+ return mA11yController.isAccessibilityTracingEnabled();
+ }
+
+ @Override
+ public void logTrace(String where) {
+ logTrace(where, "");
+ }
+
+ @Override
+ public void logTrace(String where, String callingParams) {
+ mA11yController.logTrace(where, callingParams, "".getBytes(),
+ Binder.getCallingUid(), Thread.currentThread().getStackTrace());
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 675626841d17..7d75b738d818 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -53,6 +53,10 @@ import java.util.Set;
*/
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
+ private static final String TRACE_A11Y_SERVICE_CONNECTION =
+ LOG_TAG + ".IAccessibilityServiceConnection";
+ private static final String TRACE_A11Y_SERVICE_CLIENT =
+ LOG_TAG + ".IAccessibilityServiceClient";
/*
Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
lists of bound and binding services. These are freed on user changes, but just in case it
@@ -70,11 +74,12 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
- WindowManagerInternal windowManagerInternal,
+ AccessibilityTrace trace, WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm,
ActivityTaskManagerInternal activityTaskManagerService) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
+ securityPolicy, systemSupport, trace, windowManagerInternal, systemActionPerfomer,
+ awm);
mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
mIntent = new Intent().setComponent(mComponentName);
mMainHandler = mainHandler;
@@ -132,6 +137,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void disableSelf() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf");
+ }
synchronized (mLock) {
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
@@ -210,6 +218,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
return;
}
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", "
+ + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ }
serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
} catch (RemoteException re) {
Slog.w(LOG_TAG, "Error while setting connection for service: "
@@ -252,6 +264,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean setSoftKeyboardShowMode(int showMode) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode",
+ "showMode=" + showMode);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -264,12 +280,19 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public int getSoftKeyboardShowMode() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode");
+ }
final AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
}
@Override
public boolean switchToInputMethod(String imeId) {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod",
+ "imeId=" + imeId);
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -288,6 +311,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean isAccessibilityButtonAvailable() {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable");
+ }
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return false;
@@ -347,6 +373,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT
+ + ".onFingerprintCapturingGesturesChanged", String.valueOf(active));
+ }
mServiceInterface.onFingerprintCapturingGesturesChanged(active);
} catch (RemoteException e) {
}
@@ -364,6 +394,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture",
+ String.valueOf(gesture));
+ }
mServiceInterface.onFingerprintGesture(gesture);
} catch (RemoteException e) {
}
@@ -382,6 +416,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
try {
+ if (mTrace.isA11yTracingEnabled()) {
+ mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult",
+ sequence + ", false");
+ }
mServiceInterface.onPerformGestureResult(sequence, false);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending motion event injection failure to "
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
new file mode 100644
index 000000000000..0c03877d6e44
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accessibility;
+
+/**
+ * Interface to log accessibility trace.
+ */
+public interface AccessibilityTrace {
+ /**
+ * Whether the trace is enabled.
+ */
+ boolean isA11yTracingEnabled();
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ */
+ void logTrace(String where);
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ */
+ void logTrace(String where, String callingParams);
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 4473754e2b68..9547280018e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -53,6 +53,8 @@ class UiAutomationManager {
private AbstractAccessibilityServiceConnection.SystemSupport mSystemSupport;
+ private AccessibilityTrace mTrace;
+
private int mUiAutomationFlags;
UiAutomationManager(Object lock) {
@@ -89,6 +91,7 @@ class UiAutomationManager {
int id, Handler mainHandler,
AccessibilitySecurityPolicy securityPolicy,
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
+ AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager awm, int flags) {
@@ -111,13 +114,14 @@ class UiAutomationManager {
mUiAutomationFlags = flags;
mSystemSupport = systemSupport;
+ mTrace = trace;
// Ignore registering UiAutomation if it is not allowed to use the accessibility
// subsystem.
if (!useAccessibility()) {
return;
}
mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
- mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
+ mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
systemActionPerformer, awm);
mUiAutomationServiceOwner = owner;
mUiAutomationServiceInfo = accessibilityServiceInfo;
@@ -239,11 +243,12 @@ class UiAutomationManager {
UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo,
int id, Handler mainHandler, Object lock,
AccessibilitySecurityPolicy securityPolicy,
- SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
+ SystemSupport systemSupport, AccessibilityTrace trace,
+ WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm) {
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, systemActionPerformer,
- awm);
+ securityPolicy, systemSupport, trace, windowManagerInternal,
+ systemActionPerformer, awm);
mMainHandler = mainHandler;
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 21cae453d702..a3a0cb402c76 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1330,7 +1330,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
deviceProfile, FgThread.getExecutor(), desc -> {
try {
- result.complete(desc);
+ result.complete(String.valueOf(desc));
} catch (Exception e) {
result.completeExceptionally(e);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 02930dc238ba..f4a8ccd184e5 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -69,6 +69,7 @@ import android.provider.Settings;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.IDataShareReadAdapter;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
@@ -81,6 +82,7 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.contentcapture.IDataShareWriteAdapter;
import com.android.internal.annotations.GuardedBy;
@@ -134,6 +136,9 @@ public final class ContentCaptureManagerService extends
private final LocalService mLocalService = new LocalService();
+ private final ContentCaptureManagerServiceStub mContentCaptureManagerServiceStub =
+ new ContentCaptureManagerServiceStub();
+
@Nullable
final LocalLog mRequestsHistory;
@@ -224,8 +229,7 @@ public final class ContentCaptureManagerService extends
@Override // from SystemService
public void onStart() {
- publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
- new ContentCaptureManagerServiceStub());
+ publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE, mContentCaptureManagerServiceStub);
publishLocalService(ContentCaptureManagerInternal.class, mLocalService);
}
@@ -492,6 +496,19 @@ public final class ContentCaptureManagerService extends
}
}
+ void updateOptions(String packageName, ContentCaptureOptions options) {
+ ArraySet<CallbackRecord> records;
+ synchronized (mLock) {
+ records = mContentCaptureManagerServiceStub.mCallbacks.get(packageName);
+ if (records != null) {
+ int N = records.size();
+ for (int i = 0; i < N; i++) {
+ records.valueAt(i).setContentCaptureOptions(options);
+ }
+ }
+ }
+ }
+
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -599,6 +616,8 @@ public final class ContentCaptureManagerService extends
}
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
+ @GuardedBy("mLock")
+ private final ArrayMap<String, ArraySet<CallbackRecord>> mCallbacks = new ArrayMap<>();
@Override
public void startSession(@NonNull IBinder activityToken,
@@ -755,6 +774,46 @@ public final class ContentCaptureManagerService extends
}
@Override
+ public void registerContentCaptureOptionsCallback(@NonNull String packageName,
+ IContentCaptureOptionsCallback callback) {
+ assertCalledByPackageOwner(packageName);
+
+ CallbackRecord record = new CallbackRecord(callback, packageName);
+ record.registerObserver();
+
+ synchronized (mLock) {
+ ArraySet<CallbackRecord> records = mCallbacks.get(packageName);
+ if (records == null) {
+ records = new ArraySet<>();
+ }
+ records.add(record);
+ mCallbacks.put(packageName, records);
+ }
+
+ // Set options here in case it was updated before this was registered.
+ final int userId = UserHandle.getCallingUserId();
+ final ContentCaptureOptions options = mGlobalContentCaptureOptions.getOptions(userId,
+ packageName);
+ if (options != null) {
+ record.setContentCaptureOptions(options);
+ }
+ }
+
+ private void unregisterContentCaptureOptionsCallback(CallbackRecord record) {
+ synchronized (mLock) {
+ ArraySet<CallbackRecord> records = mCallbacks.get(record.mPackageName);
+ if (records != null) {
+ records.remove(record);
+ }
+
+ if (records == null || records.isEmpty()) {
+ mCallbacks.remove(record.mPackageName);
+ }
+ }
+ record.unregisterObserver();
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
@@ -1218,4 +1277,39 @@ public final class ContentCaptureManagerService extends
mDataShareRequest.getPackageName());
}
}
+
+ private final class CallbackRecord implements IBinder.DeathRecipient {
+ private final String mPackageName;
+ private final IContentCaptureOptionsCallback mCallback;
+
+ private CallbackRecord(IContentCaptureOptionsCallback callback, String packageName) {
+ mCallback = callback;
+ mPackageName = packageName;
+ }
+
+ private void setContentCaptureOptions(ContentCaptureOptions options) {
+ try {
+ mCallback.setContentCaptureOptions(options);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
+ }
+ }
+
+ private void registerObserver() {
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to register callback cleanup " + e);
+ }
+ }
+
+ private void unregisterObserver() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ mContentCaptureManagerServiceStub.unregisterContentCaptureOptionsCallback(this);
+ }
+ }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 53cdc330cf9e..225a8d48114b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -597,9 +597,15 @@ final class ContentCapturePerUserService
? "null_activities" : activities.size() + " activities") + ")"
+ " for user " + mUserId);
}
+
+ ArraySet<String> oldList =
+ mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
+
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
+ updateContentCaptureOptions(oldList);
+
// Must disable session that are not the allowlist anymore...
final int numSessions = mSessions.size();
if (numSessions <= 0) return;
@@ -671,5 +677,23 @@ final class ContentCapturePerUserService
ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
flushMetrics, options, flushReason);
}
+
+ /** Updates {@link ContentCaptureOptions} for all newly added packages on allowlist. */
+ private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) {
+ ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions
+ .getWhitelistedPackages(mUserId);
+
+ if (oldList != null && adding != null) {
+ adding.removeAll(oldList);
+ }
+
+ int N = adding != null ? adding.size() : 0;
+ for (int i = 0; i < N; i++) {
+ String packageName = adding.valueAt(i);
+ ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions
+ .getOptions(mUserId, packageName);
+ mMaster.updateOptions(packageName, options);
+ }
+ }
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b67bdc20f7fa..99ce2db006ce 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -97,6 +97,7 @@ java_library_static {
":platform-compat-config",
":platform-compat-overrides",
":display-device-config",
+ ":display-layout-config",
":cec-config",
":device-state-config",
"java/com/android/server/EventLogTags.logtags",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 986e2acf6029..4ca6f73755f0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -70,6 +70,7 @@ import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -95,7 +96,6 @@ import android.net.INetworkActivityListener;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.INetworkStatsService;
import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
@@ -190,7 +190,6 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.BitUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -331,7 +330,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
protected IDnsResolver mDnsResolver;
@VisibleForTesting
protected INetd mNetd;
- private INetworkStatsService mStatsService;
+ private NetworkStatsManager mStatsManager;
private NetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
private final NetdCallback mNetdCallback;
@@ -1042,15 +1041,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- public ConnectivityService(Context context, INetworkStatsService statsService) {
- this(context, statsService, getDnsResolver(context), new IpConnectivityLog(),
+ public ConnectivityService(Context context) {
+ this(context, getDnsResolver(context), new IpConnectivityLog(),
NetdService.getInstance(), new Dependencies());
}
@VisibleForTesting
- protected ConnectivityService(Context context, INetworkStatsService statsService,
- IDnsResolver dnsresolver, IpConnectivityLog logger,
- INetd netd, Dependencies deps) {
+ protected ConnectivityService(Context context, IDnsResolver dnsresolver,
+ IpConnectivityLog logger, INetd netd, Dependencies deps) {
if (DBG) log("ConnectivityService starting up");
mDeps = Objects.requireNonNull(deps, "missing Dependencies");
@@ -1096,7 +1094,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO: Consider making the timer customizable.
mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
- mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+ mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mPolicyManagerInternal = Objects.requireNonNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
@@ -1480,7 +1478,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull
private NetworkInfo filterNetworkInfo(@NonNull NetworkInfo networkInfo, int type,
@NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
- NetworkInfo filtered = new NetworkInfo(networkInfo);
+ final NetworkInfo filtered = new NetworkInfo(networkInfo);
+ // Many legacy types (e.g,. TYPE_MOBILE_HIPRI) are not actually a property of the network
+ // but only exists if an app asks about them or requests them. Ensure the requesting app
+ // gets the type it asks for.
filtered.setType(type);
final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)
? DetailedState.BLOCKED
@@ -2387,13 +2388,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
final BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
options = opts.toBundle();
- final IBatteryStats bs = mDeps.getBatteryStatsService();
- try {
- bs.noteConnectivityChanged(intent.getIntExtra(
- ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
- ni.getState().toString());
- } catch (RemoteException e) {
- }
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
try {
@@ -3193,16 +3187,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Invoke ConnectivityReport generation for this Network test event.
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
if (nai == null) return;
- final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
- ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
- new ConnectivityReportEvent(p.timestampMillis, nai));
final PersistableBundle extras = new PersistableBundle();
extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
- m.setData(new Bundle(extras));
+ ConnectivityReportEvent reportEvent =
+ new ConnectivityReportEvent(p.timestampMillis, nai, extras);
+ final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED, reportEvent);
mConnectivityDiagnosticsHandler.sendMessage(m);
}
@@ -3289,8 +3283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, detectionMethod, netId,
- p.timestampMillis);
- msg.setData(new Bundle(extras));
+ new Pair<>(p.timestampMillis, extras));
// NetworkStateTrackerHandler currently doesn't take any actions based on data
// stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
@@ -3831,7 +3824,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
removeListenRequestFromNetworks(req);
}
}
- mDefaultNetworkRequests.remove(nri);
+ if (mDefaultNetworkRequests.remove(nri)) {
+ // If this request was one of the defaults, then the UID rules need to be updated
+ // WARNING : if the app(s) for which this network request is the default are doing
+ // traffic, this will kill their connected sockets, even if an equivalent request
+ // is going to be reinstated right away ; unconnected traffic will go on the default
+ // until the new default is set, which will happen very soon.
+ // TODO : The only way out of this is to diff old defaults and new defaults, and only
+ // remove ranges for those requests that won't have a replacement
+ final NetworkAgentInfo satisfier = nri.getSatisfier();
+ if (null != satisfier) {
+ try {
+ mNetd.networkRemoveUidRanges(satisfier.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ } catch (RemoteException e) {
+ loge("Exception setting network preference default network", e);
+ }
+ }
+ }
mNetworkRequestCounter.decrementCount(nri.mUid);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
@@ -4144,13 +4154,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// nai.networkMonitor() is thread-safe
return nai.networkMonitor();
}
-
- @Override
- public void logEvent(int eventId, String packageName) {
- enforceSettingsPermission();
-
- new MetricsLogger().action(eventId, packageName);
- }
}
public boolean avoidBadWifi() {
@@ -4480,16 +4483,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
break;
- case EVENT_SET_OEM_NETWORK_PREFERENCE:
+ case EVENT_SET_OEM_NETWORK_PREFERENCE: {
final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
(Pair<OemNetworkPreferences,
IOnSetOemNetworkPreferenceListener>) msg.obj;
- try {
- handleSetOemNetworkPreference(arg.first, arg.second);
- } catch (RemoteException e) {
- loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e);
- }
+ handleSetOemNetworkPreference(arg.first, arg.second);
break;
+ }
case EVENT_REPORT_NETWORK_ACTIVITY:
mNetworkActivityTracker.handleReportNetworkActivity();
break;
@@ -5247,11 +5247,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
mNetworkRequestForCallback = nri.getNetworkRequestForCallback();
+ // Note here that the satisfier may have corresponded to an old request, that
+ // this code doesn't try to take over. While it is a small discrepancy in the
+ // structure of these requests, it will be fixed by the next rematch and it's
+ // not as bad as having an NRI not storing its real satisfier.
+ // Fixing this discrepancy would require figuring out in the copying code what
+ // is the new request satisfied by this, which is a bit complex and not very
+ // useful as no code is using it until rematch fixes it.
+ mSatisfier = nri.mSatisfier;
mMessenger = nri.mMessenger;
mBinder = nri.mBinder;
mPid = nri.mPid;
mUid = nri.mUid;
mPendingIntent = nri.mPendingIntent;
+ mNetworkRequestCounter.incrementCountOrThrow(mUid);
mCallingAttributionTag = nri.mCallingAttributionTag;
}
@@ -5298,6 +5307,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
public String toString() {
return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+ (mActiveRequest == null ? null : mActiveRequest.requestId)
+ + " callback request Id: "
+ + mNetworkRequestForCallback.requestId
+ " " + mRequests
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
@@ -7154,7 +7165,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
toUidRangeStableParcels(nri.getUids()));
}
} catch (RemoteException | ServiceSpecificException e) {
- loge("Exception setting OEM network preference default network :" + e);
+ loge("Exception setting OEM network preference default network", e);
}
}
@@ -7209,13 +7220,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static class NetworkReassignment {
static class RequestReassignment {
@NonNull public final NetworkRequestInfo mNetworkRequestInfo;
- @NonNull public final NetworkRequest mOldNetworkRequest;
- @NonNull public final NetworkRequest mNewNetworkRequest;
+ @Nullable public final NetworkRequest mOldNetworkRequest;
+ @Nullable public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
- @NonNull final NetworkRequest oldNetworkRequest,
- @NonNull final NetworkRequest newNetworkRequest,
+ @Nullable final NetworkRequest oldNetworkRequest,
+ @Nullable final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
mNetworkRequestInfo = networkRequestInfo;
@@ -7226,7 +7237,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
public String toString() {
- return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ final NetworkRequest requestToShow = null != mNewNetworkRequest
+ ? mNewNetworkRequest : mNetworkRequestInfo.mRequests.get(0);
+ return requestToShow.requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7286,14 +7299,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
- @NonNull final NetworkRequest previousRequest,
- @NonNull final NetworkRequest newRequest,
+ @Nullable final NetworkRequest previousRequest,
+ @Nullable final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
if (VDBG) log("rematch for " + newSatisfier.toShortString());
- if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
+ if (null != previousRequest && null != previousSatisfier) {
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
@@ -7310,12 +7323,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
}
+ // if newSatisfier is not null, then newRequest may not be null.
newSatisfier.unlingerRequest(newRequest.requestId);
if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ newRequest);
}
- } else if (null != previousSatisfier) {
+ } else if (null != previousRequest && null != previousSatisfier) {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
+ " request " + previousRequest.requestId);
@@ -7913,7 +7927,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*
* Must be called on the handler thread.
*/
- private Network[] getDefaultNetworks() {
+ @NonNull
+ private ArrayList<Network> getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
final ArrayList<Network> defaultNetworks = new ArrayList<>();
final Set<Integer> activeNetIds = new ArraySet<>();
@@ -7927,7 +7942,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
defaultNetworks.add(nai.network);
}
}
- return defaultNetworks.toArray(new Network[0]);
+ return defaultNetworks;
}
/**
@@ -7952,8 +7967,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
state.legacyNetworkType);
snapshots.add(snapshot);
}
- mStatsService.forceUpdateIfaces(getDefaultNetworks(), snapshots.toArray(
- new NetworkStateSnapshot[0]), activeIface, underlyingNetworkInfos);
+ mStatsManager.notifyNetworkStatus(getDefaultNetworks(),
+ snapshots, activeIface, Arrays.asList(underlyingNetworkInfos));
} catch (Exception ignored) {
}
}
@@ -8272,24 +8287,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
final ConnectivityReportEvent reportEvent =
(ConnectivityReportEvent) msg.obj;
- // This is safe because {@link
- // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
- // PersistableBundle and converts it to the Bundle in the incoming Message. If
- // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
- // not be set. This is also safe, as msg.getData() will return an empty Bundle.
- final PersistableBundle extras = new PersistableBundle(msg.getData());
- handleNetworkTestedWithExtras(reportEvent, extras);
+ handleNetworkTestedWithExtras(reportEvent, reportEvent.mExtras);
break;
}
case EVENT_DATA_STALL_SUSPECTED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ final Pair<Long, PersistableBundle> arg =
+ (Pair<Long, PersistableBundle>) msg.obj;
if (nai == null) break;
- // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
- // receives a PersistableBundle and converts it to the Bundle in the incoming
- // Message.
- final PersistableBundle extras = new PersistableBundle(msg.getData());
- handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+ handleDataStallSuspected(nai, arg.first, msg.arg1, arg.second);
break;
}
case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
@@ -8353,10 +8360,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static class ConnectivityReportEvent {
private final long mTimestampMillis;
@NonNull private final NetworkAgentInfo mNai;
+ private final PersistableBundle mExtras;
- private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+ private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai,
+ PersistableBundle p) {
mTimestampMillis = timestampMillis;
mNai = nai;
+ mExtras = p;
}
}
@@ -9033,7 +9043,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleSetOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
- @NonNull final IOnSetOemNetworkPreferenceListener listener) throws RemoteException {
+ @Nullable final IOnSetOemNetworkPreferenceListener listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (DBG) {
log("set OEM network preferences :" + preference.toString());
@@ -9045,7 +9055,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO http://b/176496396 persist data to shared preferences.
if (null != listener) {
- listener.onComplete();
+ try {
+ listener.onComplete();
+ } catch (RemoteException e) {
+ loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e);
+ }
}
}
@@ -9061,10 +9075,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDefaultNetworkRequests.addAll(nris);
final ArraySet<NetworkRequestInfo> perAppCallbackRequestsToUpdate =
getPerAppCallbackRequestsToUpdate();
- handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
final ArraySet<NetworkRequestInfo> nrisToRegister = new ArraySet<>(nris);
nrisToRegister.addAll(
createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
+ handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
handleRegisterNetworkRequests(nrisToRegister);
}
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index 097441f706e6..b9922087109f 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -20,8 +20,6 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.content.Context;
-import android.net.INetworkStatsService;
-import android.os.ServiceManager;
import android.util.Log;
/**
@@ -37,7 +35,7 @@ public final class ConnectivityServiceInitializer extends SystemService {
// Load JNI libraries used by ConnectivityService and its dependencies
System.loadLibrary("service-connectivity");
// TODO: Define formal APIs to get the needed services.
- mConnectivity = new ConnectivityService(context, getNetworkStatsService());
+ mConnectivity = new ConnectivityService(context);
}
@Override
@@ -46,9 +44,4 @@ public final class ConnectivityServiceInitializer extends SystemService {
publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
}
-
- private INetworkStatsService getNetworkStatsService() {
- return INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c5233f43dcb9..27b648e53a38 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -578,6 +578,12 @@ class StorageManagerService extends IStorageManager.Stub
*/
private static final int PBKDF2_HASH_ROUNDS = 1024;
+ private static final String ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY =
+ "anr_delay_millis";
+
+ private static final String ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY =
+ "anr_delay_notify_external_storage_service";
+
/**
* Mounted OBB tracking information. Used to track the current state of all
* OBBs.
@@ -948,25 +954,51 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- // TODO(b/170486601): Check transcoding status based on events pushed from the MediaProvider
private class ExternalStorageServiceAnrController implements AnrController {
@Override
public long getAnrDelayMillis(String packageName, int uid) {
- int delay = SystemProperties.getInt("sys.fuse.transcode_anr_delay", 0);
- Log.d(TAG, "getAnrDelayMillis: " + packageName + ". Delaying for " + delay + "ms");
+ if (!isAppIoBlocked(uid)) {
+ return 0;
+ }
+
+ int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+ Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
return delay;
}
@Override
public void onAnrDelayStarted(String packageName, int uid) {
- Log.d(TAG, "onAnrDelayStarted: " + packageName);
+ if (!isAppIoBlocked(uid)) {
+ return;
+ }
+
+ boolean notifyExternalStorageService = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY, true);
+ if (notifyExternalStorageService) {
+ Slog.d(TAG, "onAnrDelayStarted for " + packageName
+ + ". Notifying external storage service");
+ try {
+ mStorageSessionController.notifyAnrDelayStarted(packageName, uid, 0 /* tid */,
+ StorageManager.APP_IO_BLOCKED_REASON_TRANSCODING);
+ } catch (ExternalStorageServiceException e) {
+ Slog.e(TAG, "Failed to notify ANR delay started for " + packageName, e);
+ }
+ } else {
+ // TODO(b/170973510): Implement framework spinning dialog for ANR delay
+ }
}
@Override
public boolean onAnrDelayCompleted(String packageName, int uid) {
- boolean show = SystemProperties.getBoolean("sys.fuse.transcode_anr_dialog_show", true);
- Log.d(TAG, "onAnrDelayCompleted: " + packageName + ". Show: " + show);
- return show;
+ if (isAppIoBlocked(uid)) {
+ Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Showing ANR dialog...");
+ return true;
+ } else {
+ Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Skipping ANR dialog...");
+ return false;
+ }
}
}
@@ -4690,5 +4722,19 @@ class StorageManagerService extends IStorageManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public List<String> getPrimaryVolumeIds() {
+ final List<String> primaryVolumeIds = new ArrayList<>();
+ synchronized (mLock) {
+ for (int i = 0; i < mVolumes.size(); i++) {
+ final VolumeInfo vol = mVolumes.valueAt(i);
+ if (vol.isPrimary()) {
+ primaryVolumeIds.add(vol.getId());
+ }
+ }
+ }
+ return primaryVolumeIds;
+ }
}
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 8d5d3d939e4b..ad2f52401e93 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -35,6 +35,7 @@ import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnManager;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
@@ -724,6 +725,26 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
+ private boolean isCallbackPermissioned(
+ @NonNull VcnStatusCallbackInfo cbInfo, @NonNull ParcelUuid subgroup) {
+ if (!subgroup.equals(cbInfo.mSubGroup)) {
+ return false;
+ }
+
+ if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subgroup, cbInfo.mPkgName)) {
+ return false;
+ }
+
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ cbInfo.mPkgName,
+ "VcnStatusCallback" /* featureId */,
+ cbInfo.mUid,
+ null /* message */)) {
+ return false;
+ }
+ return true;
+ }
+
/** Registers the provided callback for receiving VCN status updates. */
@Override
public void registerVcnStatusCallback(
@@ -758,6 +779,27 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
mRegisteredStatusCallbacks.put(cbBinder, cbInfo);
+
+ // now that callback is registered, send it the VCN's current status
+ final VcnConfig vcnConfig = mConfigs.get(subGroup);
+ final Vcn vcn = mVcns.get(subGroup);
+ final int vcnStatus;
+ if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) {
+ vcnStatus = VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
+ } else if (vcn == null) {
+ vcnStatus = VcnManager.VCN_STATUS_CODE_INACTIVE;
+ } else if (vcn.isActive()) {
+ vcnStatus = VcnManager.VCN_STATUS_CODE_ACTIVE;
+ } else {
+ // TODO(b/181789060): create Vcn.getStatus() and Log.WTF() for unknown status
+ vcnStatus = VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+ }
+
+ try {
+ cbInfo.mCallback.onVcnStatusChanged(vcnStatus);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "VcnStatusCallback threw on VCN status change", e);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -806,26 +848,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
}
- private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
- if (!mSubGroup.equals(cbInfo.mSubGroup)) {
- return false;
- }
-
- if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
- mSubGroup, cbInfo.mPkgName)) {
- return false;
- }
-
- if (!mLocationPermissionChecker.checkLocationPermission(
- cbInfo.mPkgName,
- "VcnStatusCallback" /* featureId */,
- cbInfo.mUid,
- null /* message */)) {
- return false;
- }
- return true;
- }
-
@Override
public void onEnteredSafeMode() {
synchronized (mLock) {
@@ -838,7 +860,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
- if (isCallbackPermissioned(cbInfo)) {
+ if (isCallbackPermissioned(cbInfo, mSubGroup)) {
Binder.withCleanCallingIdentity(
() ->
cbInfo.mCallback.onVcnStatusChanged(
@@ -862,7 +884,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
- if (isCallbackPermissioned(cbInfo)) {
+ if (isCallbackPermissioned(cbInfo, mSubGroup)) {
Binder.withCleanCallingIdentity(
() ->
cbInfo.mCallback.onGatewayConnectionError(
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 5d89bf1b1d82..56aabc208027 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -47,7 +47,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
-import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -60,6 +59,7 @@ import com.android.internal.net.VpnProfile;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.LockdownVpnTracker;
import java.io.FileDescriptor;
@@ -83,7 +83,7 @@ public class VpnManagerService extends IVpnManager.Stub {
private final Dependencies mDeps;
private final ConnectivityManager mCm;
- private final KeyStore mKeyStore;
+ private final VpnProfileStore mVpnProfileStore;
private final INetworkManagementService mNMS;
private final INetd mNetd;
private final UserManager mUserManager;
@@ -114,9 +114,9 @@ public class VpnManagerService extends IVpnManager.Stub {
return new HandlerThread("VpnManagerService");
}
- /** Returns the KeyStore instance to be used by this class. */
- public KeyStore getKeyStore() {
- return KeyStore.getInstance();
+ /** Return the VpnProfileStore to be used by this class */
+ public VpnProfileStore getVpnProfileStore() {
+ return new VpnProfileStore();
}
public INetd getNetd() {
@@ -135,7 +135,7 @@ public class VpnManagerService extends IVpnManager.Stub {
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
- mKeyStore = mDeps.getKeyStore();
+ mVpnProfileStore = mDeps.getVpnProfileStore();
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
mCm = mContext.getSystemService(ConnectivityManager.class);
mNMS = mDeps.getINetworkManagementService();
@@ -289,7 +289,7 @@ public class VpnManagerService extends IVpnManager.Stub {
public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
- return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
+ return mVpns.get(user).provisionVpnProfile(packageName, profile);
}
}
@@ -307,7 +307,7 @@ public class VpnManagerService extends IVpnManager.Stub {
public void deleteVpnProfile(@NonNull String packageName) {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
- mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
+ mVpns.get(user).deleteVpnProfile(packageName);
}
}
@@ -325,7 +325,7 @@ public class VpnManagerService extends IVpnManager.Stub {
final int user = UserHandle.getUserId(mDeps.getCallingUid());
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startVpnProfile(packageName, mKeyStore);
+ mVpns.get(user).startVpnProfile(packageName);
}
}
@@ -358,7 +358,7 @@ public class VpnManagerService extends IVpnManager.Stub {
}
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
+ mVpns.get(user).startLegacyVpn(profile, null /* underlying */, egress);
}
}
@@ -396,7 +396,7 @@ public class VpnManagerService extends IVpnManager.Stub {
}
private boolean isLockdownVpnEnabled() {
- return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
+ return mVpnProfileStore.get(Credentials.LOCKDOWN_VPN) != null;
}
@Override
@@ -417,14 +417,14 @@ public class VpnManagerService extends IVpnManager.Stub {
return true;
}
- byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ byte[] profileTag = mVpnProfileStore.get(Credentials.LOCKDOWN_VPN);
if (profileTag == null) {
loge("Lockdown VPN configured but cannot be read from keystore");
return false;
}
String profileName = new String(profileTag);
final VpnProfile profile = VpnProfile.decode(
- profileName, mKeyStore.get(Credentials.VPN + profileName));
+ profileName, mVpnProfileStore.get(Credentials.VPN + profileName));
if (profile == null) {
loge("Lockdown VPN configured invalid profile " + profileName);
setLockdownTracker(null);
@@ -437,7 +437,7 @@ public class VpnManagerService extends IVpnManager.Stub {
return false;
}
setLockdownTracker(
- new LockdownVpnTracker(mContext, mHandler, mKeyStore, vpn, profile));
+ new LockdownVpnTracker(mContext, mHandler, vpn, profile));
}
return true;
@@ -495,7 +495,7 @@ public class VpnManagerService extends IVpnManager.Stub {
return false;
}
- return vpn.startAlwaysOnVpn(mKeyStore);
+ return vpn.startAlwaysOnVpn();
}
}
@@ -510,7 +510,7 @@ public class VpnManagerService extends IVpnManager.Stub {
logw("User " + userId + " has no Vpn configuration");
return false;
}
- return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
+ return vpn.isAlwaysOnPackageSupported(packageName);
}
}
@@ -531,11 +531,11 @@ public class VpnManagerService extends IVpnManager.Stub {
logw("User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownAllowlist, mKeyStore)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownAllowlist)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
+ vpn.setAlwaysOnPackage(null, false, null);
return false;
}
}
@@ -705,7 +705,8 @@ public class VpnManagerService extends IVpnManager.Stub {
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId,
+ new VpnProfileStore());
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
updateLockdownVpn();
@@ -777,7 +778,7 @@ public class VpnManagerService extends IVpnManager.Stub {
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
log("Restarting always-on VPN package " + packageName + " for user "
+ userId);
- vpn.startAlwaysOnVpn(mKeyStore);
+ vpn.startAlwaysOnVpn();
}
}
}
@@ -798,7 +799,7 @@ public class VpnManagerService extends IVpnManager.Stub {
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
log("Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
+ vpn.setAlwaysOnPackage(null, false, null);
}
}
}
@@ -843,7 +844,7 @@ public class VpnManagerService extends IVpnManager.Stub {
if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
final long ident = Binder.clearCallingIdentity();
try {
- mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mVpnProfileStore.remove(Credentials.LOCKDOWN_VPN);
mLockdownEnabled = false;
setLockdownTracker(null);
} finally {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d998ebbf4aff..277cb8c877dd 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -86,6 +86,7 @@ import android.app.Service;
import android.app.ServiceStartArgs;
import android.app.admin.DevicePolicyEventLogger;
import android.app.compat.CompatChanges;
+import android.app.usage.UsageEvents;
import android.appwidget.AppWidgetManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -2464,6 +2465,10 @@ public final class ActiveServices {
s.setAllowedBgFgsStartsByBinding(true);
}
+ if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) {
+ s.isNotAppComponentUsage = true;
+ }
+
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app.mServices, c, true);
}
@@ -3332,6 +3337,14 @@ public final class ActiveServices {
return msg;
}
+ // Report usage if binding is from a different package except for explicitly exempted
+ // bindings
+ if (!r.appInfo.packageName.equals(r.mRecentCallingPackage)
+ && !r.isNotAppComponentUsage) {
+ mAm.mUsageStatsService.reportEvent(
+ r.packageName, r.userId, UsageEvents.Event.APP_COMPONENT_USED);
+ }
+
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8a4fa20cd30..874e5272764c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2684,6 +2684,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
+ if (event == Event.ACTIVITY_RESUMED) {
+ // Report component usage as an activity is an app component
+ mUsageStatsService.reportEvent(
+ activity.getPackageName(), userId, Event.APP_COMPONENT_USED);
+ }
}
ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
@@ -6099,6 +6104,10 @@ public class ActivityManagerService extends IActivityManager.Stub
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
+ // Report usage as process is persistent and being started.
+ mUsageStatsService.reportEvent(info.packageName, UserHandle.getUserId(app.uid),
+ Event.APP_COMPONENT_USED);
+
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c971bd2ab6d5..5ad77a3a412a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -168,6 +168,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
private int mTaskId;
private boolean mIsTaskOverlay;
private boolean mIsLockTask;
+ private boolean mAsync;
private BroadcastOptions mBroadcastOptions;
final boolean mDumping;
@@ -342,6 +343,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
mTaskId = INVALID_TASK_ID;
mIsTaskOverlay = false;
mIsLockTask = false;
+ mAsync = false;
mBroadcastOptions = null;
return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@@ -406,6 +408,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
mBroadcastOptions = BroadcastOptions.makeBasic();
}
mBroadcastOptions.setBackgroundActivityStartsAllowed(true);
+ } else if (opt.equals("--async")) {
+ mAsync = true;
} else {
return false;
}
@@ -756,7 +760,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
mUserId);
- receiver.waitForFinish();
+ if (!mAsync) {
+ receiver.waitForFinish();
+ }
return 0;
}
@@ -3180,13 +3186,17 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Stop a Service. Options are:");
pw.println(" --user <USER_ID> | current: Specify which user to run as; if not");
pw.println(" specified then run as the current user.");
- pw.println(" broadcast [--user <USER_ID> | all | current] <INTENT>");
+ pw.println(" broadcast [--user <USER_ID> | all | current]");
+ pw.println(" [--receiver-permission <PERMISSION>]");
+ pw.println(" [--allow-background-activity-starts]");
+ pw.println(" [--async] <INTENT>");
pw.println(" Send a broadcast Intent. Options are:");
pw.println(" --user <USER_ID> | all | current: Specify which user to send to; if not");
pw.println(" specified then send to all users.");
pw.println(" --receiver-permission <PERMISSION>: Require receiver to hold permission.");
pw.println(" --allow-background-activity-starts: The receiver may start activities");
pw.println(" even if in the background.");
+ pw.println(" --async: Send without waiting for the completion of the receiver.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 167c2b1ad66c..82f72e8cc1ac 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+
+import android.annotation.NonNull;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -25,7 +28,9 @@ import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.State;
import android.hardware.power.stats.StateResidency;
import android.hardware.power.stats.StateResidencyResult;
+import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -77,6 +82,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.net.BaseNetworkObserver;
@@ -291,6 +297,23 @@ public final class BatteryStatsService extends IBatteryStats.Stub
return builder.toString();
}
+ private ConnectivityManager.NetworkCallback mNetworkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ final String state = networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ ? "CONNECTED" : "SUSPENDED";
+ noteConnectivityChanged(NetworkCapabilitiesUtils.getDisplayTransport(
+ networkCapabilities.getTransportTypes()), state);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ noteConnectivityChanged(-1, "DISCONNECTED");
+ }
+ };
+
BatteryStatsService(Context context, File systemDir, Handler handler) {
// BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
mContext = context;
@@ -330,8 +353,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mWorker.systemServicesReady();
final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
nms.registerObserver(mActivityChangeObserver);
+ cm.registerDefaultNetworkCallback(mNetworkCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 29061930cd84..06cacc70a9b8 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -29,6 +29,7 @@ import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.PendingIntent;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
@@ -52,6 +53,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.permission.IPermissionManager;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -1634,6 +1636,13 @@ public final class BroadcastQueue {
brOptions.getTemporaryAppAllowlistReason());
}
+ // Report that a component is used for explicit broadcasts.
+ if (!r.intent.isExcludingStopped() && r.curComponent != null
+ && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
+ mService.mUsageStatsService.reportEvent(
+ r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
+ }
+
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index f43c7f6278c9..2c8794d75795 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -32,6 +32,7 @@ import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.app.ContentProviderHolder;
import android.app.IApplicationThread;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -57,6 +58,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -412,6 +414,12 @@ public class ContentProviderHelper {
final long origId = Binder.clearCallingIdentity();
try {
+ if (!TextUtils.equals(cpr.appInfo.packageName, callingPackage)) {
+ // Report component used since a content provider is being bound.
+ mService.mUsageStatsService.reportEvent(
+ cpr.appInfo.packageName, userId, Event.APP_COMPONENT_USED);
+ }
+
// Content provider is now in use, its package can't be stopped.
try {
checkTime(startTime,
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 3258f8af0da2..d03a47afed8a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -329,6 +329,22 @@ class ProcessErrorStateRecord {
info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
}
+ // Retrieve controller with max ANR delay from AnrControllers
+ // Note that we retrieve the controller before dumping stacks because dumping stacks can
+ // take a few seconds, after which the cause of the ANR delay might have completed and
+ // there might no longer be a valid ANR controller to cancel the dialog in that case
+ AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo);
+ long anrDialogDelayMs = 0;
+ if (anrController != null) {
+ String packageName = aInfo.packageName;
+ int uid = aInfo.uid;
+ anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid);
+ // Might execute an async binder call to a system app to show an interim
+ // ANR progress UI
+ anrController.onAnrDelayStarted(packageName, uid);
+ Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName);
+ }
+
StringBuilder report = new StringBuilder();
report.append(MemoryPressureUtil.currentPsiState());
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
@@ -417,20 +433,6 @@ class ProcessErrorStateRecord {
return;
}
- // Retrieve max ANR delay from AnrControllers without the mService lock since the
- // controllers might in turn call into apps
- AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo);
- long anrDialogDelayMs = 0;
- if (anrController != null) {
- String packageName = aInfo.packageName;
- int uid = aInfo.uid;
- anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid);
- // Might execute an async binder call to a system app to show an interim
- // ANR progress UI
- anrController.onAnrDelayStarted(packageName, uid);
- Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName);
- }
-
synchronized (mService) {
// mBatteryStatsService can be null if the AMS is constructed with injector only. This
// will only happen in tests.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3ab95d131fad..9cd9902f4995 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -107,6 +107,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean delayed; // are we waiting to start this service in the background?
boolean fgRequired; // is the service required to go foreground after starting?
boolean fgWaiting; // is a timeout for going foreground already scheduled?
+ boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 44dcc205a9d0..11125dd55665 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -843,11 +843,15 @@ public class AppOpsService extends IAppOpsService.Stub {
public void accessed(int proxyUid, @Nullable String proxyPackageName,
@Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
@OpFlags int flags) {
- accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
+ long accessTime = System.currentTimeMillis();
+ accessed(accessTime, -1, proxyUid, proxyPackageName,
proxyAttributionTag, uidState, flags);
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
tag, uidState, flags);
+
+ mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid,
+ parent.packageName, parent.op, tag, flags, uidState, accessTime, -1);
}
/**
@@ -1004,8 +1008,10 @@ public class AppOpsService extends IAppOpsService.Stub {
OpEventProxyInfo proxyCopy = event.getProxy() != null
? new OpEventProxyInfo(event.getProxy()) : null;
+ long accessDurationMillis =
+ SystemClock.elapsedRealtime() - event.getStartElapsedTime();
NoteOpEvent finishedEvent = new NoteOpEvent(event.getStartTime(),
- SystemClock.elapsedRealtime() - event.getStartElapsedTime(), proxyCopy);
+ accessDurationMillis, proxyCopy);
mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()),
finishedEvent);
@@ -1013,6 +1019,10 @@ public class AppOpsService extends IAppOpsService.Stub {
parent.packageName, tag, event.getUidState(),
event.getFlags(), finishedEvent.getDuration());
+ mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid,
+ parent.packageName, parent.op, tag, event.getFlags(), event.getUidState(),
+ event.getStartTime(), accessDurationMillis);
+
mInProgressStartOpEventPool.release(event);
if (mInProgressEvents.isEmpty()) {
@@ -2087,8 +2097,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void getHistoricalOps(int uid, String packageName, String attributionTag,
- List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
- int flags, RemoteCallback callback) {
+ List<String> opNames, int dataType, int filter, long beginTimeMillis,
+ long endTimeMillis, int flags, RemoteCallback callback) {
PackageManager pm = mContext.getPackageManager();
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
@@ -2120,14 +2130,14 @@ public class AppOpsService extends IAppOpsService.Stub {
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
- mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, filter,
- beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
+ filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@Override
public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
- List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
- int flags, RemoteCallback callback) {
+ List<String> opNames, int dataType, int filter, long beginTimeMillis,
+ long endTimeMillis, int flags, RemoteCallback callback) {
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2140,7 +2150,7 @@ public class AppOpsService extends IAppOpsService.Stub {
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
- mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray,
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@@ -4759,6 +4769,7 @@ public class AppOpsService extends IAppOpsService.Stub {
mFile.failWrite(stream);
}
}
+ mHistoricalRegistry.mDiscreteRegistry.writeAndClearAccessHistory();
}
static class Shell extends ShellCommand {
@@ -6115,6 +6126,7 @@ public class AppOpsService extends IAppOpsService.Stub {
"clearHistory");
// Must not hold the appops lock
mHistoricalRegistry.clearHistory();
+ mHistoricalRegistry.mDiscreteRegistry.clearHistory();
}
@Override
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
new file mode 100644
index 000000000000..76990453ee03
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
+import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+
+import static java.lang.Math.max;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class manages information about recent accesses to ops for
+ * permission usage timeline.
+ *
+ * The timeline history is kept for limited time (initial default is 24 hours) and
+ * discarded after that.
+ *
+ * Every time state is saved (default is 30 minutes), memory state is dumped to a
+ * new file and memory state is cleared. Files older than time limit are deleted
+ * during the process.
+ *
+ * When request comes in, files are read and requested information is collected
+ * and delivered.
+ */
+
+final class DiscreteRegistry {
+ static final String TIMELINE_FILE_SUFFIX = "tl";
+ private static final String TAG = DiscreteRegistry.class.getSimpleName();
+
+ private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
+ private static final String TAG_HISTORY = "h";
+ private static final String ATTR_VERSION = "v";
+ private static final int CURRENT_VERSION = 1;
+
+ private static final String TAG_UID = "u";
+ private static final String ATTR_UID = "ui";
+
+ private static final String TAG_PACKAGE = "p";
+ private static final String ATTR_PACKAGE_NAME = "pn";
+
+ private static final String TAG_OP = "o";
+ private static final String ATTR_OP_ID = "op";
+
+ private static final String TAG_TAG = "a";
+ private static final String ATTR_TAG = "at";
+
+ private static final String TAG_ENTRY = "e";
+ private static final String ATTR_NOTE_TIME = "nt";
+ private static final String ATTR_NOTE_DURATION = "nd";
+ private static final String ATTR_UID_STATE = "us";
+ private static final String ATTR_FLAGS = "f";
+
+ // Lock for read/write access to on disk state
+ private final Object mOnDiskLock = new Object();
+
+ //Lock for read/write access to in memory state
+ private final @NonNull Object mInMemoryLock;
+
+ @GuardedBy("mOnDiskLock")
+ private final File mDiscreteAccessDir;
+
+ @GuardedBy("mInMemoryLock")
+ private DiscreteOps mDiscreteOps;
+
+ DiscreteRegistry(Object inMemoryLock) {
+ mInMemoryLock = inMemoryLock;
+ mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
+ "discrete");
+ createDiscreteAccessDir();
+ mDiscreteOps = new DiscreteOps();
+ }
+
+ private void createDiscreteAccessDir() {
+ if (!mDiscreteAccessDir.exists()) {
+ if (!mDiscreteAccessDir.mkdirs()) {
+ Slog.e(TAG, "Failed to create DiscreteRegistry directory");
+ }
+ FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
+ }
+ }
+
+ void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
+ @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
+ long accessDuration) {
+ if (!isDiscreteOp(op, uid, flags)) {
+ return;
+ }
+ synchronized (mInMemoryLock) {
+ mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState,
+ accessTime, accessDuration);
+ }
+ }
+
+ void writeAndClearAccessHistory() {
+ synchronized (mOnDiskLock) {
+ final File[] files = mDiscreteAccessDir.listFiles();
+ if (files != null && files.length > 0) {
+ for (File f : files) {
+ final String fileName = f.getName();
+ if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
+ continue;
+ }
+ try {
+ long timestamp = Long.valueOf(fileName.substring(0,
+ fileName.length() - TIMELINE_FILE_SUFFIX.length()));
+ if (Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
+ f.delete();
+ Slog.e(TAG, "Deleting file " + fileName);
+
+ }
+ } catch (Throwable t) {
+ Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
+ + t.getStackTrace());
+ }
+ }
+ }
+ }
+ DiscreteOps discreteOps;
+ synchronized (mInMemoryLock) {
+ discreteOps = mDiscreteOps;
+ mDiscreteOps = new DiscreteOps();
+ }
+ if (discreteOps.isEmpty()) {
+ return;
+ }
+ long currentTimeStamp = Instant.now().toEpochMilli();
+ try {
+ final File file = new File(mDiscreteAccessDir, currentTimeStamp + TIMELINE_FILE_SUFFIX);
+ discreteOps.writeToFile(file);
+ } catch (Throwable t) {
+ Slog.e(TAG,
+ "Error writing timeline state: " + t.getMessage() + " "
+ + Arrays.toString(t.getStackTrace()));
+ }
+ }
+
+ void getHistoricalDiscreteOps(AppOpsManager.HistoricalOps result, long beginTimeMillis,
+ long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ writeAndClearAccessHistory();
+ DiscreteOps discreteOps = new DiscreteOps();
+ readDiscreteOpsFromDisk(discreteOps, beginTimeMillis, endTimeMillis, filter, uidFilter,
+ packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ discreteOps.applyToHistoricalOps(result);
+ return;
+ }
+
+ private void readDiscreteOpsFromDisk(DiscreteOps discreteOps, long beginTimeMillis,
+ long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ synchronized (mOnDiskLock) {
+ long historyBeginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ ChronoUnit.MILLIS).toEpochMilli();
+ if (historyBeginTimeMillis > endTimeMillis) {
+ return;
+ }
+ beginTimeMillis = max(beginTimeMillis, historyBeginTimeMillis);
+
+ final File[] files = mDiscreteAccessDir.listFiles();
+ if (files != null && files.length > 0) {
+ for (File f : files) {
+ final String fileName = f.getName();
+ if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
+ continue;
+ }
+ long timestamp = Long.valueOf(fileName.substring(0,
+ fileName.length() - TIMELINE_FILE_SUFFIX.length()));
+ if (timestamp < beginTimeMillis) {
+ continue;
+ }
+ discreteOps.readFromFile(f, beginTimeMillis, endTimeMillis, filter, uidFilter,
+ packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ }
+ }
+ }
+ }
+
+ void clearHistory() {
+ synchronized (mOnDiskLock) {
+ synchronized (mInMemoryLock) {
+ mDiscreteOps = new DiscreteOps();
+ }
+ FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
+ createDiscreteAccessDir();
+ }
+ }
+
+ public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
+ if (!isDiscreteOp(op)) {
+ return false;
+ }
+ if (!isDiscreteUid(uid)) {
+ return false;
+ }
+ if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ static boolean isDiscreteOp(int op) {
+ if (op != OP_CAMERA && op != OP_RECORD_AUDIO && op != OP_FINE_LOCATION
+ && op != OP_COARSE_LOCATION) {
+ return false;
+ }
+ return true;
+ }
+
+ static boolean isDiscreteUid(int uid) {
+ if (uid < Process.FIRST_APPLICATION_UID) {
+ return false;
+ }
+ return true;
+ }
+
+ private final class DiscreteOps {
+ ArrayMap<Integer, DiscreteUidOps> mUids;
+
+ DiscreteOps() {
+ mUids = new ArrayMap<>();
+ }
+
+ void addDiscreteAccess(int op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
+ getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
+ uidState, accessTime, accessDuration);
+ }
+
+ private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
+ int nUids = mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i));
+ }
+ }
+
+ private void writeToFile(File f) throws Exception {
+ FileOutputStream stream = new FileOutputStream(f);
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
+
+ out.startDocument(null, true);
+ out.startTag(null, TAG_HISTORY);
+ out.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
+
+ int nUids = mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ out.startTag(null, TAG_UID);
+ out.attributeInt(null, ATTR_UID, mUids.keyAt(i));
+ mUids.valueAt(i).serialize(out);
+ out.endTag(null, TAG_UID);
+ }
+ out.endTag(null, TAG_HISTORY);
+ out.endDocument();
+ stream.close();
+ }
+
+ private DiscreteUidOps getOrCreateDiscreteUidOps(int uid) {
+ DiscreteUidOps result = mUids.get(uid);
+ if (result == null) {
+ result = new DiscreteUidOps();
+ mUids.put(uid, result);
+ }
+ return result;
+ }
+
+ boolean isEmpty() {
+ return mUids.isEmpty();
+ }
+
+ private void readFromFile(File f, long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ try {
+ FileInputStream stream = new FileInputStream(f);
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+ XmlUtils.beginDocument(parser, TAG_HISTORY);
+
+ // We haven't released version 1 and have more detailed
+ // accounting - just nuke the current state
+ final int version = parser.getAttributeInt(null, ATTR_VERSION);
+ if (version != CURRENT_VERSION) {
+ throw new IllegalStateException("Dropping unsupported discrete history " + f);
+ }
+
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_UID.equals(parser.getName())) {
+ int uid = parser.getAttributeInt(null, ATTR_UID, -1);
+ if ((filter & FILTER_BY_UID) != 0 && uid != uidFilter) {
+ continue;
+ }
+ getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis,
+ endTimeMillis, filter, packageNameFilter, opNamesFilter,
+ attributionTagFilter, flagsFilter);
+ }
+ }
+ } catch (Throwable t) {
+ Slog.e(TAG, "Failed to read file " + f.getName() + " " + t.getMessage() + " "
+ + Arrays.toString(t.getStackTrace()));
+ }
+
+ }
+ }
+
+ private final class DiscreteUidOps {
+ ArrayMap<String, DiscretePackageOps> mPackages;
+
+ DiscreteUidOps() {
+ mPackages = new ArrayMap<>();
+ }
+
+ void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
+ @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
+ long accessTime, long accessDuration) {
+ getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags,
+ uidState, accessTime, accessDuration);
+ }
+
+ private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) {
+ DiscretePackageOps result = mPackages.get(packageName);
+ if (result == null) {
+ result = new DiscretePackageOps();
+ mPackages.put(packageName, result);
+ }
+ return result;
+ }
+
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) {
+ int nPackages = mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i));
+ }
+ }
+
+ void serialize(TypedXmlSerializer out) throws Exception {
+ int nPackages = mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ out.startTag(null, TAG_PACKAGE);
+ out.attribute(null, ATTR_PACKAGE_NAME, mPackages.keyAt(i));
+ mPackages.valueAt(i).serialize(out);
+ out.endTag(null, TAG_PACKAGE);
+ }
+ }
+
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
+ long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String packageNameFilter,
+ @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
+ @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_PACKAGE.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0
+ && !packageName.equals(packageNameFilter)) {
+ continue;
+ }
+ getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis,
+ endTimeMillis, filter, opNamesFilter, attributionTagFilter,
+ flagsFilter);
+ }
+ }
+ }
+ }
+
+ private final class DiscretePackageOps {
+ ArrayMap<Integer, DiscreteOp> mPackageOps;
+
+ DiscretePackageOps() {
+ mPackageOps = new ArrayMap<>();
+ }
+
+ void addDiscreteAccess(int op, @Nullable String attributionTag,
+ @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
+ long accessTime, long accessDuration) {
+ getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime,
+ accessDuration);
+ }
+
+ private DiscreteOp getOrCreateDiscreteOp(int op) {
+ DiscreteOp result = mPackageOps.get(op);
+ if (result == null) {
+ result = new DiscreteOp();
+ mPackageOps.put(op, result);
+ }
+ return result;
+ }
+
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+ @NonNull String packageName) {
+ int nPackageOps = mPackageOps.size();
+ for (int i = 0; i < nPackageOps; i++) {
+ mPackageOps.valueAt(i).applyToHistory(result, uid, packageName,
+ mPackageOps.keyAt(i));
+ }
+ }
+
+ void serialize(TypedXmlSerializer out) throws Exception {
+ int nOps = mPackageOps.size();
+ for (int i = 0; i < nOps; i++) {
+ out.startTag(null, TAG_OP);
+ out.attributeInt(null, ATTR_OP_ID, mPackageOps.keyAt(i));
+ mPackageOps.valueAt(i).serialize(out);
+ out.endTag(null, TAG_OP);
+ }
+ }
+
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
+ @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_OP.equals(parser.getName())) {
+ int op = parser.getAttributeInt(null, ATTR_OP_ID);
+ if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
+ AppOpsManager.opToPublicName(op))) {
+ continue;
+ }
+ getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis, endTimeMillis,
+ filter, attributionTagFilter, flagsFilter);
+ }
+ }
+ }
+ }
+
+ private final class DiscreteOp {
+ ArrayMap<String, List<DiscreteOpEvent>> mAttributedOps;
+
+ DiscreteOp() {
+ mAttributedOps = new ArrayMap<>();
+ }
+
+ void addDiscreteAccess(@Nullable String attributionTag,
+ @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
+ long accessTime, long accessDuration) {
+ List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
+ attributionTag);
+ accessTime = Instant.ofEpochMilli(accessTime).truncatedTo(
+ ChronoUnit.MINUTES).toEpochMilli();
+
+ int nAttributedOps = attributedOps.size();
+ for (int i = nAttributedOps - 1; i >= 0; i--) {
+ DiscreteOpEvent previousOp = attributedOps.get(i);
+ if (previousOp.mNoteTime < accessTime) {
+ break;
+ }
+ if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
+ return;
+ }
+ }
+ attributedOps.add(new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ }
+
+ private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
+ List<DiscreteOpEvent> result = mAttributedOps.get(attributionTag);
+ if (result == null) {
+ result = new ArrayList<>();
+ mAttributedOps.put(attributionTag, result);
+ }
+ return result;
+ }
+
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+ @NonNull String packageName, int op) {
+ int nOps = mAttributedOps.size();
+ for (int i = 0; i < nOps; i++) {
+ String tag = mAttributedOps.keyAt(i);
+ List<DiscreteOpEvent> events = mAttributedOps.valueAt(i);
+ int nEvents = events.size();
+ for (int j = 0; j < nEvents; j++) {
+ DiscreteOpEvent event = events.get(j);
+ result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
+ event.mOpFlag, event.mNoteTime, event.mNoteDuration);
+ }
+ }
+ }
+
+ void serialize(TypedXmlSerializer out) throws Exception {
+ int nAttributions = mAttributedOps.size();
+ for (int i = 0; i < nAttributions; i++) {
+ out.startTag(null, TAG_TAG);
+ String tag = mAttributedOps.keyAt(i);
+ if (tag != null) {
+ out.attribute(null, ATTR_TAG, mAttributedOps.keyAt(i));
+ }
+ List<DiscreteOpEvent> ops = mAttributedOps.valueAt(i);
+ int nOps = ops.size();
+ for (int j = 0; j < nOps; j++) {
+ out.startTag(null, TAG_ENTRY);
+ ops.get(j).serialize(out);
+ out.endTag(null, TAG_ENTRY);
+ }
+ out.endTag(null, TAG_TAG);
+ }
+ }
+
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String attributionTagFilter,
+ @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_TAG.equals(parser.getName())) {
+ String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !attributionTag.equals(
+ attributionTagFilter)) {
+ continue;
+ }
+ List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
+ attributionTag);
+ int innerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, innerDepth)) {
+ if (TAG_ENTRY.equals(parser.getName())) {
+ long noteTime = parser.getAttributeLong(null, ATTR_NOTE_TIME);
+ long noteDuration = parser.getAttributeLong(null, ATTR_NOTE_DURATION,
+ -1);
+ int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
+ int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
+ if ((flagsFilter & opFlags) == 0) {
+ continue;
+ }
+ if ((noteTime + noteDuration < beginTimeMillis
+ && noteTime > endTimeMillis)) {
+ continue;
+ }
+ DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
+ uidState, opFlags);
+ events.add(event);
+ }
+ }
+ Collections.sort(events, (a, b) -> a.mNoteTime < b.mNoteTime ? -1
+ : (a.mNoteTime == b.mNoteTime ? 0 : 1));
+ }
+ }
+ }
+ }
+
+ private final class DiscreteOpEvent {
+ final long mNoteTime;
+ final long mNoteDuration;
+ final @AppOpsManager.UidState int mUidState;
+ final @AppOpsManager.OpFlags int mOpFlag;
+
+ DiscreteOpEvent(long noteTime, long noteDuration, @AppOpsManager.UidState int uidState,
+ @AppOpsManager.OpFlags int opFlag) {
+ mNoteTime = noteTime;
+ mNoteDuration = noteDuration;
+ mUidState = uidState;
+ mOpFlag = opFlag;
+ }
+
+ private void serialize(TypedXmlSerializer out) throws Exception {
+ out.attributeLong(null, ATTR_NOTE_TIME, mNoteTime);
+ if (mNoteDuration != -1) {
+ out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration);
+ }
+ out.attributeInt(null, ATTR_UID_STATE, mUidState);
+ out.attributeInt(null, ATTR_FLAGS, mOpFlag);
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 17fd32c57e09..1c43fedd3112 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -19,6 +19,8 @@ import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HISTORY_FLAG_AGGREGATE;
+import static android.app.AppOpsManager.HISTORY_FLAG_DISCRETE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +32,7 @@ import android.app.AppOpsManager.HistoricalOpsRequestFilter;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.HistoricalUidOps;
import android.app.AppOpsManager.OpFlags;
+import android.app.AppOpsManager.OpHistoryFlags;
import android.app.AppOpsManager.UidState;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -61,9 +64,7 @@ import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -71,7 +72,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -85,7 +85,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
- * This class managers historical app op state. This includes reading, persistence,
+ * This class manages historical app op state. This includes reading, persistence,
* accounting, querying.
* <p>
* The history is kept forever in multiple files. Each file time contains the
@@ -138,6 +138,8 @@ final class HistoricalRegistry {
private static final String PARAMETER_ASSIGNMENT = "=";
private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
+ volatile @NonNull DiscreteRegistry mDiscreteRegistry;
+
@GuardedBy("mLock")
private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
@@ -199,6 +201,7 @@ final class HistoricalRegistry {
HistoricalRegistry(@NonNull Object lock) {
mInMemoryLock = lock;
+ mDiscreteRegistry = new DiscreteRegistry(lock);
}
HistoricalRegistry(@NonNull HistoricalRegistry other) {
@@ -352,36 +355,49 @@ final class HistoricalRegistry {
}
}
- void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
+ void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String[] opNames,
- @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
+ @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
+ long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
+ @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
}
- synchronized (mOnDiskLock) {
- synchronized (mInMemoryLock) {
- if (!isPersistenceInitializedMLocked()) {
- Slog.e(LOG_TAG, "Interaction before persistence initialized");
- callback.sendResult(new Bundle());
- return;
+ final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
+
+ if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
+ synchronized (mOnDiskLock) {
+ synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ callback.sendResult(new Bundle());
+ return;
+ }
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName,
+ attributionTag,
+ opNames, filter, beginTimeMillis, endTimeMillis, flags);
+
}
- final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
- opNames, filter, beginTimeMillis, endTimeMillis, flags);
- final Bundle payload = new Bundle();
- payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
- callback.sendResult(payload);
}
}
+
+ if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
+ mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis,
+ filter, uid, packageName, opNames, attributionTag,
+ flags);
+ }
+
+ final Bundle payload = new Bundle();
+ payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
+ callback.sendResult(payload);
}
- void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String attributionTag,
- @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
- long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
- @NonNull RemoteCallback callback) {
+ void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable String[] opNames, @OpHistoryFlags int historyFlags,
+ @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
+ @OpFlags int flags, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -392,6 +408,8 @@ final class HistoricalRegistry {
endTimeMillis = currentTimeMillis;
}
+ final Bundle payload = new Bundle();
+
// Argument times are based off epoch start while our internal store is
// based off now, so take this into account.
final long inMemoryAdjBeginTimeMillis = Math.max(currentTimeMillis - endTimeMillis, 0);
@@ -399,55 +417,63 @@ final class HistoricalRegistry {
final HistoricalOps result = new HistoricalOps(inMemoryAdjBeginTimeMillis,
inMemoryAdjEndTimeMillis);
- synchronized (mOnDiskLock) {
- final List<HistoricalOps> pendingWrites;
- final HistoricalOps currentOps;
- boolean collectOpsFromDisk;
-
- synchronized (mInMemoryLock) {
- if (!isPersistenceInitializedMLocked()) {
- Slog.e(LOG_TAG, "Interaction before persistence initialized");
- callback.sendResult(new Bundle());
- return;
- }
-
- currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis);
- if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis()
- || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
- // Some of the current batch falls into the query, so extract that.
- final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
- currentOpsCopy.filter(uid, packageName, attributionTag, opNames, filter,
- inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis);
- result.merge(currentOpsCopy);
- }
- pendingWrites = new ArrayList<>(mPendingWrites);
- mPendingWrites.clear();
- collectOpsFromDisk = inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis();
- }
+ if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
+ mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis,
+ filter, uid, packageName, opNames, attributionTag, flags);
+ }
- // If the query was only for in-memory state - done.
- if (collectOpsFromDisk) {
- // If there is a write in flight we need to force it now
- persistPendingHistory(pendingWrites);
- // Collect persisted state.
- final long onDiskAndInMemoryOffsetMillis = currentTimeMillis
- - mNextPersistDueTimeMillis + mBaseSnapshotInterval;
- final long onDiskAdjBeginTimeMillis = Math.max(inMemoryAdjBeginTimeMillis
- - onDiskAndInMemoryOffsetMillis, 0);
- final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
- - onDiskAndInMemoryOffsetMillis, 0);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
- opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
- }
+ if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
+ synchronized (mOnDiskLock) {
+ final List<HistoricalOps> pendingWrites;
+ final HistoricalOps currentOps;
+ boolean collectOpsFromDisk;
- // Rebase the result time to be since epoch.
- result.setBeginAndEndTime(beginTimeMillis, endTimeMillis);
+ synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ callback.sendResult(new Bundle());
+ return;
+ }
- // Send back the result.
- final Bundle payload = new Bundle();
- payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
- callback.sendResult(payload);
- }
+ currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis);
+ if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis()
+ || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
+ // Some of the current batch falls into the query, so extract that.
+ final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
+ currentOpsCopy.filter(uid, packageName, attributionTag, opNames,
+ historyFlags, filter, inMemoryAdjBeginTimeMillis,
+ inMemoryAdjEndTimeMillis);
+ result.merge(currentOpsCopy);
+ }
+ pendingWrites = new ArrayList<>(mPendingWrites);
+ mPendingWrites.clear();
+ collectOpsFromDisk = inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis();
+ }
+
+ // If the query was only for in-memory state - done.
+ if (collectOpsFromDisk) {
+ // If there is a write in flight we need to force it now
+ persistPendingHistory(pendingWrites);
+ // Collect persisted state.
+ final long onDiskAndInMemoryOffsetMillis = currentTimeMillis
+ - mNextPersistDueTimeMillis + mBaseSnapshotInterval;
+ final long onDiskAdjBeginTimeMillis = Math.max(inMemoryAdjBeginTimeMillis
+ - onDiskAndInMemoryOffsetMillis, 0);
+ final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
+ - onDiskAndInMemoryOffsetMillis, 0);
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName,
+ attributionTag,
+ opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis,
+ flags);
+ }
+ }
+ }
+ // Rebase the result time to be since epoch.
+ result.setBeginAndEndTime(beginTimeMillis, endTimeMillis);
+
+ // Send back the result.
+ payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
+ callback.sendResult(payload);
}
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
@@ -692,6 +718,7 @@ final class HistoricalRegistry {
}
persistPendingHistory(pendingWrites);
}
+ mDiscreteRegistry.writeAndClearAccessHistory();
}
private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index e19745e5c578..050b28b363d2 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -33,6 +33,7 @@ import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -337,6 +338,168 @@ public class AuthService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public CharSequence getButtonLabel(
+ int userId,
+ String opPackageName,
+ @Authenticators.Types int authenticators) throws RemoteException {
+
+ // Only allow internal clients to call getButtonLabel with a different userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ @BiometricAuthenticator.Modality final int modality =
+ mBiometricService.getCurrentModality(
+ opPackageName, userId, callingUserId, authenticators);
+
+ final String result;
+ switch (getCredentialBackupModality(modality)) {
+ case BiometricAuthenticator.TYPE_NONE:
+ result = null;
+ break;
+ case BiometricAuthenticator.TYPE_CREDENTIAL:
+ result = getContext().getString(R.string.screen_lock_app_setting_name);
+ break;
+ case BiometricAuthenticator.TYPE_FINGERPRINT:
+ result = getContext().getString(R.string.fingerprint_app_setting_name);
+ break;
+ case BiometricAuthenticator.TYPE_FACE:
+ result = getContext().getString(R.string.face_app_setting_name);
+ break;
+ default:
+ result = getContext().getString(R.string.biometric_app_setting_name);
+ break;
+ }
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public CharSequence getPromptMessage(
+ int userId,
+ String opPackageName,
+ @Authenticators.Types int authenticators) throws RemoteException {
+
+ // Only allow internal clients to call getButtonLabel with a different userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ @BiometricAuthenticator.Modality final int modality =
+ mBiometricService.getCurrentModality(
+ opPackageName, userId, callingUserId, authenticators);
+
+ final String result;
+ switch (getCredentialBackupModality(modality)) {
+ case BiometricAuthenticator.TYPE_NONE:
+ result = null;
+ break;
+ case BiometricAuthenticator.TYPE_CREDENTIAL:
+ result = getContext().getString(
+ R.string.screen_lock_dialog_default_subtitle);
+ break;
+ case BiometricAuthenticator.TYPE_FINGERPRINT:
+ result = getContext().getString(
+ R.string.fingerprint_dialog_default_subtitle);
+ break;
+ case BiometricAuthenticator.TYPE_FACE:
+ result = getContext().getString(R.string.face_dialog_default_subtitle);
+ break;
+ default:
+ result = getContext().getString(R.string.biometric_dialog_default_subtitle);
+ break;
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public CharSequence getSettingName(
+ int userId,
+ String opPackageName,
+ @Authenticators.Types int authenticators) throws RemoteException {
+
+ // Only allow internal clients to call getButtonLabel with a different userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ @BiometricAuthenticator.Modality final int modality =
+ mBiometricService.getSupportedModalities(authenticators);
+
+ final String result;
+ switch (modality) {
+ // Handle the case of a single supported modality.
+ case BiometricAuthenticator.TYPE_NONE:
+ result = null;
+ break;
+ case BiometricAuthenticator.TYPE_CREDENTIAL:
+ result = getContext().getString(R.string.screen_lock_app_setting_name);
+ break;
+ case BiometricAuthenticator.TYPE_IRIS:
+ result = getContext().getString(R.string.biometric_app_setting_name);
+ break;
+ case BiometricAuthenticator.TYPE_FINGERPRINT:
+ result = getContext().getString(R.string.fingerprint_app_setting_name);
+ break;
+ case BiometricAuthenticator.TYPE_FACE:
+ result = getContext().getString(R.string.face_app_setting_name);
+ break;
+
+ // Handle other possible modality combinations.
+ default:
+ if ((modality & BiometricAuthenticator.TYPE_CREDENTIAL) == 0) {
+ // 2+ biometric modalities are supported (but not device credential).
+ result = getContext().getString(R.string.biometric_app_setting_name);
+ } else {
+ @BiometricAuthenticator.Modality final int biometricModality =
+ modality & ~BiometricAuthenticator.TYPE_CREDENTIAL;
+ if (biometricModality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ // Only device credential and fingerprint are supported.
+ result = getContext().getString(
+ R.string.fingerprint_or_screen_lock_app_setting_name);
+ } else if (biometricModality == BiometricAuthenticator.TYPE_FACE) {
+ // Only device credential and face are supported.
+ result = getContext().getString(
+ R.string.face_or_screen_lock_app_setting_name);
+ } else {
+ // Device credential and 1+ other biometric(s) are supported.
+ result = getContext().getString(
+ R.string.biometric_or_screen_lock_app_setting_name);
+ }
+ }
+ break;
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
public AuthService(Context context) {
@@ -442,4 +605,10 @@ public class AuthService extends SystemService {
return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
}
+
+ @BiometricAuthenticator.Modality
+ private static int getCredentialBackupModality(@BiometricAuthenticator.Modality int modality) {
+ return modality == BiometricAuthenticator.TYPE_CREDENTIAL
+ ? modality : (modality & ~BiometricAuthenticator.TYPE_CREDENTIAL);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 00a4e43f347d..a88820988ef7 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -666,14 +666,9 @@ public class BiometricService extends SystemService {
throw new SecurityException("Invalid authenticator configuration");
}
- final PromptInfo promptInfo = new PromptInfo();
- promptInfo.setAuthenticators(authenticators);
-
try {
- PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
- mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
- opPackageName,
- false /* checkDevicePolicyManager */);
+ final PreAuthInfo preAuthInfo =
+ createPreAuthInfo(opPackageName, userId, authenticators);
return preAuthInfo.getCanAuthenticateResult();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
@@ -807,6 +802,64 @@ public class BiometricService extends SystemService {
return Authenticators.EMPTY_SET;
}
+ @Override // Binder call
+ public int getCurrentModality(
+ String opPackageName,
+ int userId,
+ int callingUserId,
+ @Authenticators.Types int authenticators) {
+
+ checkInternalPermission();
+
+ Slog.d(TAG, "getCurrentModality: User=" + userId
+ + ", Caller=" + callingUserId
+ + ", Authenticators=" + authenticators);
+
+ if (!Utils.isValidAuthenticatorConfig(authenticators)) {
+ throw new SecurityException("Invalid authenticator configuration");
+ }
+
+ try {
+ final PreAuthInfo preAuthInfo =
+ createPreAuthInfo(opPackageName, userId, authenticators);
+ return preAuthInfo.getPreAuthenticateStatus().first;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ return BiometricAuthenticator.TYPE_NONE;
+ }
+ }
+
+ @Override // Binder call
+ public int getSupportedModalities(@Authenticators.Types int authenticators) {
+ checkInternalPermission();
+
+ Slog.d(TAG, "getSupportedModalities: Authenticators=" + authenticators);
+
+ if (!Utils.isValidAuthenticatorConfig(authenticators)) {
+ throw new SecurityException("Invalid authenticator configuration");
+ }
+
+ @BiometricAuthenticator.Modality int modality =
+ Utils.isCredentialRequested(authenticators)
+ ? BiometricAuthenticator.TYPE_CREDENTIAL
+ : BiometricAuthenticator.TYPE_NONE;
+
+ if (Utils.isBiometricRequested(authenticators)) {
+ @Authenticators.Types final int requestedStrength =
+ Utils.getPublicBiometricStrength(authenticators);
+
+ // Add modalities of all biometric sensors that meet the authenticator requirements.
+ for (final BiometricSensor sensor : mSensors) {
+ @Authenticators.Types final int sensorStrength = sensor.getCurrentStrength();
+ if (Utils.isAtLeastStrength(sensorStrength, requestedStrength)) {
+ modality |= sensor.modality;
+ }
+ }
+ }
+
+ return modality;
+ }
+
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
@@ -845,6 +898,19 @@ public class BiometricService extends SystemService {
"Must have USE_BIOMETRIC_INTERNAL permission");
}
+ @NonNull
+ private PreAuthInfo createPreAuthInfo(
+ @NonNull String opPackageName,
+ int userId,
+ @Authenticators.Types int authenticators) throws RemoteException {
+
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(authenticators);
+
+ return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
+ userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */);
+ }
+
/**
* Class for injecting dependencies into BiometricService.
* TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 5cd0bbfa4500..d9e21a83e45a 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -153,6 +153,16 @@ public class Utils {
/**
* Checks if any of the publicly defined strengths are set.
*
+ * @param authenticators composed of one or more values from {@link Authenticators}
+ * @return true if biometric authentication is allowed.
+ */
+ static boolean isBiometricRequested(@Authenticators.Types int authenticators) {
+ return getPublicBiometricStrength(authenticators) != 0;
+ }
+
+ /**
+ * Checks if any of the publicly defined strengths are set.
+ *
* @param promptInfo should be first processed by
* {@link #combineAuthenticatorBundles(PromptInfo)}
* @return true if biometric authentication is allowed.
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
index 816bf2be0d69..0f5400d0f8e6 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -27,7 +27,7 @@ import android.net.QosSession;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.data.EpsBearerQosSessionAttributes;
-import android.util.Slog;
+import android.util.Log;
import java.util.Objects;
@@ -175,18 +175,14 @@ class QosCallbackAgentConnection implements IBinder.DeathRecipient {
}
private static void log(@NonNull final String msg) {
- Slog.d(TAG, msg);
+ Log.d(TAG, msg);
}
private static void logw(@NonNull final String msg) {
- Slog.w(TAG, msg);
+ Log.w(TAG, msg);
}
private static void loge(@NonNull final String msg, final Throwable t) {
- Slog.e(TAG, msg, t);
- }
-
- private static void logwtf(@NonNull final String msg) {
- Slog.wtf(TAG, msg);
+ Log.e(TAG, msg, t);
}
}
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 7ef315c469ae..8bda5323e4f8 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -29,7 +29,7 @@ import android.os.IBinder;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.util.Log;
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
import com.android.server.ConnectivityService;
import java.util.ArrayList;
@@ -156,12 +156,13 @@ public class QosCallbackTracker {
private void handleUnregisterCallback(@NonNull final IBinder binder,
final boolean sendToNetworkAgent) {
- final QosCallbackAgentConnection agentConnection =
- CollectionUtils.find(mConnections, c -> c.getBinder().equals(binder));
- if (agentConnection == null) {
- logw("handleUnregisterCallback: agentConnection is null");
+ final int connIndex =
+ CollectionUtils.indexOf(mConnections, c -> c.getBinder().equals(binder));
+ if (connIndex < 0) {
+ logw("handleUnregisterCallback: no matching agentConnection");
return;
}
+ final QosCallbackAgentConnection agentConnection = mConnections.get(connIndex);
if (DBG) {
log("handleUnregisterCallback: unregister "
@@ -226,10 +227,10 @@ public class QosCallbackTracker {
* @param network the network that was released
*/
public void handleNetworkReleased(@Nullable final Network network) {
- final List<QosCallbackAgentConnection> connections =
- CollectionUtils.filter(mConnections, ac -> ac.getNetwork().equals(network));
-
- for (final QosCallbackAgentConnection agentConnection : connections) {
+ // Iterate in reverse order as agent connections will be removed when unregistering
+ for (int i = mConnections.size() - 1; i >= 0; i--) {
+ final QosCallbackAgentConnection agentConnection = mConnections.get(i);
+ if (!agentConnection.getNetwork().equals(network)) continue;
agentConnection.sendEventQosCallbackError(
QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
@@ -247,15 +248,14 @@ public class QosCallbackTracker {
@NonNull final String logPrefix,
@NonNull final AgentConnectionAction action) {
mConnectivityServiceHandler.post(() -> {
- final QosCallbackAgentConnection ac =
- CollectionUtils.find(mConnections,
+ final int acIndex = CollectionUtils.indexOf(mConnections,
c -> c.getAgentCallbackId() == qosCallbackId);
- if (ac == null) {
+ if (acIndex == -1) {
loge(logPrefix + ": " + qosCallbackId + " missing callback id");
return;
}
- action.execute(ac);
+ action.execute(mConnections.get(acIndex));
});
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 67f495a455fb..2e61ae1b3483 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -101,7 +101,12 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.Credentials;
-import android.security.KeyStore;
+import android.security.KeyStore2;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyPermission;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -132,6 +137,12 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -157,6 +168,7 @@ public class Vpn {
private static final String TAG = "Vpn";
private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:";
private static final boolean LOGD = true;
+ private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
// Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
// the device idle allowlist during service launch and VPN bootstrap.
@@ -216,6 +228,13 @@ public class Vpn {
private final Ikev2SessionCreator mIkev2SessionCreator;
private final UserManager mUserManager;
+ private final VpnProfileStore mVpnProfileStore;
+
+ @VisibleForTesting
+ VpnProfileStore getVpnProfileStore() {
+ return mVpnProfileStore;
+ }
+
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
@@ -393,24 +412,25 @@ public class Vpn {
}
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
- @UserIdInt int userId, @NonNull KeyStore keyStore) {
- this(looper, context, new Dependencies(), netService, netd, userId, keyStore,
+ @UserIdInt int userId, VpnProfileStore vpnProfileStore) {
+ this(looper, context, new Dependencies(), netService, netd, userId, vpnProfileStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
public Vpn(Looper looper, Context context, Dependencies deps,
INetworkManagementService netService, INetd netd, @UserIdInt int userId,
- @NonNull KeyStore keyStore) {
- this(looper, context, deps, netService, netd, userId, keyStore,
+ VpnProfileStore vpnProfileStore) {
+ this(looper, context, deps, netService, netd, userId, vpnProfileStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, Dependencies deps,
INetworkManagementService netService, INetd netd,
- int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
+ int userId, VpnProfileStore vpnProfileStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
+ mVpnProfileStore = vpnProfileStore;
mContext = context;
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
@@ -446,7 +466,7 @@ public class Vpn {
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
- loadAlwaysOnPackage(keyStore);
+ loadAlwaysOnPackage();
}
/**
@@ -567,11 +587,9 @@ public class Vpn {
* </ul>
*
* @param packageName the canonical package name of the VPN app
- * @param keyStore the keystore instance to use for checking if the app has a Platform VPN
- * profile installed.
* @return {@code true} if and only if the VPN app exists and supports always-on mode
*/
- public boolean isAlwaysOnPackageSupported(String packageName, @NonNull KeyStore keyStore) {
+ public boolean isAlwaysOnPackageSupported(String packageName) {
enforceSettingsPermission();
if (packageName == null) {
@@ -580,7 +598,7 @@ public class Vpn {
final long oldId = Binder.clearCallingIdentity();
try {
- if (getVpnProfilePrivileged(packageName, keyStore) != null) {
+ if (getVpnProfilePrivileged(packageName) != null) {
return true;
}
} finally {
@@ -632,17 +650,15 @@ public class Vpn {
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownAllowlist packages to be allowed from lockdown.
- * @param keyStore the Keystore instance to use for checking of PlatformVpnProfile(s)
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
public synchronized boolean setAlwaysOnPackage(
@Nullable String packageName,
boolean lockdown,
- @Nullable List<String> lockdownAllowlist,
- @NonNull KeyStore keyStore) {
+ @Nullable List<String> lockdownAllowlist) {
enforceControlPermissionOrInternalCaller();
- if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist, keyStore)) {
+ if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) {
saveAlwaysOnPackage();
return true;
}
@@ -659,13 +675,12 @@ public class Vpn {
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownAllowlist packages to be allowed to bypass lockdown. This is only used if
* {@code lockdown} is {@code true}. Packages must not contain commas.
- * @param keyStore the system keystore instance to check for profiles
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
@GuardedBy("this")
private boolean setAlwaysOnPackageInternal(
@Nullable String packageName, boolean lockdown,
- @Nullable List<String> lockdownAllowlist, @NonNull KeyStore keyStore) {
+ @Nullable List<String> lockdownAllowlist) {
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
return false;
@@ -684,7 +699,7 @@ public class Vpn {
final VpnProfile profile;
final long oldId = Binder.clearCallingIdentity();
try {
- profile = getVpnProfilePrivileged(packageName, keyStore);
+ profile = getVpnProfilePrivileged(packageName);
} finally {
Binder.restoreCallingIdentity(oldId);
}
@@ -759,7 +774,7 @@ public class Vpn {
/** Load the always-on package and lockdown config from Settings. */
@GuardedBy("this")
- private void loadAlwaysOnPackage(@NonNull KeyStore keyStore) {
+ private void loadAlwaysOnPackage() {
final long token = Binder.clearCallingIdentity();
try {
final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
@@ -771,7 +786,7 @@ public class Vpn {
final List<String> allowedPackages = TextUtils.isEmpty(allowlistString)
? Collections.emptyList() : Arrays.asList(allowlistString.split(","));
setAlwaysOnPackageInternal(
- alwaysOnPackage, alwaysOnLockdown, allowedPackages, keyStore);
+ alwaysOnPackage, alwaysOnLockdown, allowedPackages);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -780,11 +795,10 @@ public class Vpn {
/**
* Starts the currently selected always-on VPN
*
- * @param keyStore the keyStore instance for looking up PlatformVpnProfile(s)
* @return {@code true} if the service was started, the service was already connected, or there
* was no always-on VPN to start. {@code false} otherwise.
*/
- public boolean startAlwaysOnVpn(@NonNull KeyStore keyStore) {
+ public boolean startAlwaysOnVpn() {
final String alwaysOnPackage;
synchronized (this) {
alwaysOnPackage = getAlwaysOnPackage();
@@ -793,8 +807,8 @@ public class Vpn {
return true;
}
// Remove always-on VPN if it's not supported.
- if (!isAlwaysOnPackageSupported(alwaysOnPackage, keyStore)) {
- setAlwaysOnPackage(null, false, null, keyStore);
+ if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
+ setAlwaysOnPackage(null, false, null);
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -808,10 +822,9 @@ public class Vpn {
final long oldId = Binder.clearCallingIdentity();
try {
// Prefer VPN profiles, if any exist.
- VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage, keyStore);
+ VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage);
if (profile != null) {
- startVpnProfilePrivileged(profile, alwaysOnPackage,
- null /* keyStore for private key retrieval - unneeded */);
+ startVpnProfilePrivileged(profile, alwaysOnPackage);
// If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
// correctly parsed, and the VPN has started running in a different thread. The only
@@ -2013,27 +2026,83 @@ public class Vpn {
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile, Network, LinkProperties)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+ public void startLegacyVpn(VpnProfile profile, @Nullable Network underlying,
LinkProperties egress) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
+ startLegacyVpnPrivileged(profile, underlying, egress);
} finally {
Binder.restoreCallingIdentity(token);
}
}
+ private String makeKeystoreEngineGrantString(String alias) {
+ if (alias == null) {
+ return null;
+ }
+ // If Keystore 2.0 is not enabled the legacy private key prefix is used.
+ if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ return Credentials.USER_PRIVATE_KEY + alias;
+ }
+ final KeyStore2 keystore2 = KeyStore2.getInstance();
+
+ KeyDescriptor key = new KeyDescriptor();
+ key.domain = Domain.APP;
+ key.nspace = KeyProperties.NAMESPACE_APPLICATION;
+ key.alias = alias;
+ key.blob = null;
+
+ final int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO;
+
+ try {
+ // The native vpn daemon is running as VPN_UID. This tells Keystore 2.0
+ // to allow a process running with this UID to access the key designated by
+ // the KeyDescriptor `key`. `grant` returns a new KeyDescriptor with a grant
+ // identifier. This identifier needs to be communicated to the vpn daemon.
+ key = keystore2.grant(key, android.os.Process.VPN_UID, grantAccessVector);
+ } catch (android.security.KeyStoreException e) {
+ Log.e(TAG, "Failed to get grant for keystore key.", e);
+ throw new IllegalStateException("Failed to get grant for keystore key.", e);
+ }
+
+ // Turn the grant identifier into a string as understood by the keystore boringssl engine
+ // in system/security/keystore-engine.
+ return KeyStore2.makeKeystoreEngineGrantString(key.nspace);
+ }
+
+ private String getCaCertificateFromKeystoreAsPem(@NonNull KeyStore keystore,
+ @NonNull String alias)
+ throws KeyStoreException, IOException, CertificateEncodingException {
+ if (keystore.isCertificateEntry(alias)) {
+ final Certificate cert = keystore.getCertificate(alias);
+ if (cert == null) return null;
+ return new String(Credentials.convertToPem(cert), StandardCharsets.UTF_8);
+ } else {
+ final Certificate[] certs = keystore.getCertificateChain(alias);
+ // If there is none or one entry it means there is no CA entry associated with this
+ // alias.
+ if (certs == null || certs.length <= 1) {
+ return null;
+ }
+ // If this is not a (pure) certificate entry, then there is a user certificate which
+ // will be included at the beginning of the certificate chain. But the caller of this
+ // function does not expect this certificate to be included, so we cut it off.
+ return new String(Credentials.convertToPem(
+ Arrays.copyOfRange(certs, 1, certs.length)), StandardCharsets.UTF_8);
+ }
+ }
+
/**
- * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+ * Like {@link #startLegacyVpn(VpnProfile, Network, LinkProperties)}, but does not
* check permissions under the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
- public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
+ public void startLegacyVpnPrivileged(VpnProfile profile,
@Nullable Network underlying, @NonNull LinkProperties egress) {
UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
@@ -2050,18 +2119,27 @@ public class Vpn {
String userCert = "";
String caCert = "";
String serverCert = "";
- if (!profile.ipsecUserCert.isEmpty()) {
- privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
- byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
- userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
- }
- if (!profile.ipsecCaCert.isEmpty()) {
- byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
- caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
- }
- if (!profile.ipsecServerCert.isEmpty()) {
- byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
- serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
+
+ try {
+ final KeyStore keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER);
+ keystore.load(null);
+ if (!profile.ipsecUserCert.isEmpty()) {
+ privateKey = profile.ipsecUserCert;
+ final Certificate cert = keystore.getCertificate(profile.ipsecUserCert);
+ userCert = (cert == null) ? null
+ : new String(Credentials.convertToPem(cert), StandardCharsets.UTF_8);
+ }
+ if (!profile.ipsecCaCert.isEmpty()) {
+ caCert = getCaCertificateFromKeystoreAsPem(keystore, profile.ipsecCaCert);
+ }
+ if (!profile.ipsecServerCert.isEmpty()) {
+ final Certificate cert = keystore.getCertificate(profile.ipsecServerCert);
+ serverCert = (cert == null) ? null
+ : new String(Credentials.convertToPem(cert), StandardCharsets.UTF_8);
+ }
+ } catch (CertificateException | KeyStoreException | IOException
+ | NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Failed to load credentials from AndroidKeyStore", e);
}
if (userCert == null || caCert == null || serverCert == null) {
throw new IllegalStateException("Cannot load credentials");
@@ -2082,7 +2160,7 @@ public class Vpn {
// Start VPN profile
profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
- startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
// Ikev2VpnProfiles expect a base64-encoded preshared key.
@@ -2091,7 +2169,7 @@ public class Vpn {
// Start VPN profile
profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
- startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
racoon = new String[] {
@@ -2101,8 +2179,8 @@ public class Vpn {
break;
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
racoon = new String[] {
- iface, profile.server, "udprsa", privateKey, userCert,
- caCert, serverCert, "1701",
+ iface, profile.server, "udprsa", makeKeystoreEngineGrantString(privateKey),
+ userCert, caCert, serverCert, "1701",
};
break;
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
@@ -2113,8 +2191,8 @@ public class Vpn {
break;
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
racoon = new String[] {
- iface, profile.server, "xauthrsa", privateKey, userCert,
- caCert, serverCert, profile.username, profile.password, "", gateway,
+ iface, profile.server, "xauthrsa", makeKeystoreEngineGrantString(privateKey),
+ userCert, caCert, serverCert, profile.username, profile.password, "", gateway,
};
break;
case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
@@ -3049,14 +3127,12 @@ public class Vpn {
*
* @param packageName the package name of the app provisioning this profile
* @param profile the profile to be stored and provisioned
- * @param keyStore the System keystore instance to save VPN profiles
* @returns whether or not the app has already been granted user consent
*/
public synchronized boolean provisionVpnProfile(
- @NonNull String packageName, @NonNull VpnProfile profile, @NonNull KeyStore keyStore) {
+ @NonNull String packageName, @NonNull VpnProfile profile) {
checkNotNull(packageName, "No package name provided");
checkNotNull(profile, "No profile provided");
- checkNotNull(keyStore, "KeyStore missing");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3075,11 +3151,9 @@ public class Vpn {
// Permissions checked during startVpnProfile()
Binder.withCleanCallingIdentity(
() -> {
- keyStore.put(
+ getVpnProfileStore().put(
getProfileNameForPackage(packageName),
- encodedProfile,
- Process.SYSTEM_UID,
- 0 /* flags */);
+ encodedProfile);
});
// TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
@@ -3097,12 +3171,10 @@ public class Vpn {
* Deletes an app-provisioned VPN profile.
*
* @param packageName the package name of the app provisioning this profile
- * @param keyStore the System keystore instance to save VPN profiles
*/
public synchronized void deleteVpnProfile(
- @NonNull String packageName, @NonNull KeyStore keyStore) {
+ @NonNull String packageName) {
checkNotNull(packageName, "No package name provided");
- checkNotNull(keyStore, "KeyStore missing");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3114,13 +3186,13 @@ public class Vpn {
if (isCurrentIkev2VpnLocked(packageName)) {
if (mAlwaysOn) {
// Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
- setAlwaysOnPackage(null, false, null, keyStore);
+ setAlwaysOnPackage(null, false, null);
} else {
prepareInternal(VpnConfig.LEGACY_VPN);
}
}
- keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
+ getVpnProfileStore().remove(getProfileNameForPackage(packageName));
});
}
@@ -3132,13 +3204,13 @@ public class Vpn {
*/
@VisibleForTesting
@Nullable
- VpnProfile getVpnProfilePrivileged(@NonNull String packageName, @NonNull KeyStore keyStore) {
+ VpnProfile getVpnProfilePrivileged(@NonNull String packageName) {
if (!mDeps.isCallerSystem()) {
Log.wtf(TAG, "getVpnProfilePrivileged called as non-System UID ");
return null;
}
- final byte[] encoded = keyStore.get(getProfileNameForPackage(packageName));
+ final byte[] encoded = getVpnProfileStore().get(getProfileNameForPackage(packageName));
if (encoded == null) return null;
return VpnProfile.decode("" /* Key unused */, encoded);
@@ -3152,12 +3224,10 @@ public class Vpn {
* will not match during appop checks.
*
* @param packageName the package name of the app provisioning this profile
- * @param keyStore the System keystore instance to retrieve VPN profiles
*/
public synchronized void startVpnProfile(
- @NonNull String packageName, @NonNull KeyStore keyStore) {
+ @NonNull String packageName) {
checkNotNull(packageName, "No package name provided");
- checkNotNull(keyStore, "KeyStore missing");
enforceNotRestrictedUser();
@@ -3168,18 +3238,17 @@ public class Vpn {
Binder.withCleanCallingIdentity(
() -> {
- final VpnProfile profile = getVpnProfilePrivileged(packageName, keyStore);
+ final VpnProfile profile = getVpnProfilePrivileged(packageName);
if (profile == null) {
throw new IllegalArgumentException("No profile found for " + packageName);
}
- startVpnProfilePrivileged(profile, packageName,
- null /* keyStore for private key retrieval - unneeded */);
+ startVpnProfilePrivileged(profile, packageName);
});
}
private synchronized void startVpnProfilePrivileged(
- @NonNull VpnProfile profile, @NonNull String packageName, @Nullable KeyStore keyStore) {
+ @NonNull VpnProfile profile, @NonNull String packageName) {
// Make sure VPN is prepared. This method can be called by user apps via startVpnProfile(),
// by the Setting app via startLegacyVpn(), or by ConnectivityService via
// startAlwaysOnVpn(), so this is the common place to prepare the VPN. This also has the
@@ -3210,7 +3279,7 @@ public class Vpn {
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
mVpnRunner =
- new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile, keyStore));
+ new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
mVpnRunner.start();
break;
default:
@@ -3218,7 +3287,7 @@ public class Vpn {
Log.d(TAG, "Unknown VPN profile type: " + profile.type);
break;
}
- } catch (IOException | GeneralSecurityException e) {
+ } catch (GeneralSecurityException e) {
// Reset mConfig
mConfig = null;
diff --git a/services/core/java/com/android/server/connectivity/VpnProfileStore.java b/services/core/java/com/android/server/connectivity/VpnProfileStore.java
new file mode 100644
index 000000000000..2f8aebf07e49
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/VpnProfileStore.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.security.LegacyVpnProfileStore;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Mockable indirection to the actual profile store.
+ * @hide
+ */
+public class VpnProfileStore {
+ /**
+ * Stores the profile under the alias in the profile database. Existing profiles by the
+ * same name will be replaced.
+ * @param alias The name of the profile
+ * @param profile The profile.
+ * @return true if the profile was successfully added. False otherwise.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean put(@NonNull String alias, @NonNull byte[] profile) {
+ return LegacyVpnProfileStore.put(alias, profile);
+ }
+
+ /**
+ * Retrieves a profile by the name alias from the profile database.
+ * @param alias Name of the profile to retrieve.
+ * @return The unstructured blob, that is the profile that was stored using
+ * LegacyVpnProfileStore#put or with
+ * android.security.Keystore.put(Credentials.VPN + alias).
+ * Returns null if no profile was found.
+ * @hide
+ */
+ @VisibleForTesting
+ public byte[] get(@NonNull String alias) {
+ return LegacyVpnProfileStore.get(alias);
+ }
+
+ /**
+ * Removes a profile by the name alias from the profile database.
+ * @param alias Name of the profile to be removed.
+ * @return True if a profile was removed. False if no such profile was found.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean remove(@NonNull String alias) {
+ return LegacyVpnProfileStore.remove(alias);
+ }
+
+ /**
+ * Lists the vpn profiles stored in the database.
+ * @return An array of strings representing the aliases stored in the profile database.
+ * The return value may be empty but never null.
+ * @hide
+ */
+ @VisibleForTesting
+ public @NonNull String[] list(@NonNull String prefix) {
+ return LegacyVpnProfileStore.list(prefix);
+ }
+}
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 91674cd8afa6..1acd5d097525 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -16,17 +16,26 @@
package com.android.server.display;
-import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
-import android.text.TextUtils;
+import android.os.Environment;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAddress;
+import com.android.server.display.config.layout.Layouts;
+import com.android.server.display.config.layout.XmlParser;
import com.android.server.display.layout.Layout;
-import java.util.Arrays;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.datatype.DatatypeConfigurationException;
/**
* Mapping from device states into {@link Layout}s. This allows us to map device
@@ -39,11 +48,14 @@ class DeviceStateToLayoutMap {
public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
+ private static final String CONFIG_FILE_PATH =
+ "etc/displayconfig/display_layout_configuration.xml";
+
private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
- DeviceStateToLayoutMap(Context context) {
- mLayoutMap.append(STATE_DEFAULT, new Layout());
- loadFoldedDisplayConfig(context);
+ DeviceStateToLayoutMap() {
+ loadLayoutsFromConfig();
+ createLayout(STATE_DEFAULT);
}
public void dumpLocked(IndentingPrintWriter ipw) {
@@ -76,48 +88,36 @@ class DeviceStateToLayoutMap {
}
/**
- * Loads config.xml-specified folded configurations for foldable devices.
+ * Reads display-layout-configuration files to get the layouts to use for this device.
*/
- private void loadFoldedDisplayConfig(Context context) {
- final String[] strDisplayIds = context.getResources().getStringArray(
- com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
- if (strDisplayIds.length != 2 || TextUtils.isEmpty(strDisplayIds[0])
- || TextUtils.isEmpty(strDisplayIds[1])) {
- Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(strDisplayIds)
- + "]");
- return;
- }
+ private void loadLayoutsFromConfig() {
+ final File configFile = Environment.buildPath(
+ Environment.getVendorDirectory(), CONFIG_FILE_PATH);
- final long[] displayIds;
- try {
- displayIds = new long[] {
- Long.parseLong(strDisplayIds[0]),
- Long.parseLong(strDisplayIds[1])
- };
- } catch (NumberFormatException nfe) {
- Slog.w(TAG, "Folded display config non numerical: " + Arrays.toString(strDisplayIds));
+ if (!configFile.exists()) {
return;
}
- final int[] foldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates);
- final int[] unfoldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_unfoldedDeviceStates);
- // Only add folded states if folded state config is not empty
- if (foldedDeviceStates.length == 0 || unfoldedDeviceStates.length == 0) {
- return;
- }
-
- for (int state : foldedDeviceStates) {
- // Create the folded state layout
- createLayout(state).createDisplayLocked(
- DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
- }
-
- for (int state : unfoldedDeviceStates) {
- // Create the unfolded state layout
- createLayout(state).createDisplayLocked(
- DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
+ Slog.i(TAG, "Loading display layouts from " + configFile);
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ final Layouts layouts = XmlParser.read(in);
+ if (layouts == null) {
+ Slog.i(TAG, "Display layout config not found: " + configFile);
+ return;
+ }
+ for (com.android.server.display.config.layout.Layout l : layouts.getLayout()) {
+ final int state = l.getState().intValue();
+ final Layout layout = createLayout(state);
+ for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
+ layout.createDisplayLocked(
+ DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
+ d.getIsDefault(),
+ d.getEnabled());
+ }
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing display layout config file: "
+ + configFile, e);
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ce0ba9f3231c..174d4b2fe00d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -423,7 +423,7 @@ public final class DisplayManagerService extends SystemService {
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
- mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo,
+ mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo,
new LogicalDisplayListener());
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
@@ -1178,7 +1178,10 @@ public final class DisplayManagerService extends SystemService {
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- mDisplayPowerControllers.removeReturnOld(displayId).stop();
+ final DisplayPowerController dpc = mDisplayPowerControllers.removeReturnOld(displayId);
+ if (dpc != null) {
+ dpc.stop();
+ }
mDisplayStates.delete(displayId);
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1200,9 +1203,6 @@ public final class DisplayManagerService extends SystemService {
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
- // TODO - b/170498827 The rules regarding what display state to apply to each
- // display will depend on the configuration/mapping of logical displays.
- // Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
final int state;
final int displayId = display.getDisplayIdLocked();
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 2bcc35cf176f..d6826be248df 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -16,7 +16,6 @@
package com.android.server.display;
-import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.os.SystemProperties;
import android.text.TextUtils;
@@ -90,7 +89,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
private final Listener mListener;
- private final int[] mFoldedDeviceStates;
/**
* Has an entry for every logical display that the rest of the system has been notified about.
@@ -122,16 +120,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private Layout mCurrentLayout = null;
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
- LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
+ LogicalDisplayMapper(DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
mListener = listener;
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mDisplayDeviceRepo.addListener(this);
-
- mFoldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates);
-
- mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(context);
+ mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
}
@Override
@@ -470,10 +464,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
/**
- * Resets the current layout in preparation for a new layout; essentially just marks
- * all the currently layed out displays as disabled. This ensures the display devices
- * are turned off. If they are meant to be used in the new layout,
- * {@link #applyLayoutLocked()} will reenabled them.
+ * Resets the current layout in preparation for a new layout. Layouts can specify if some
+ * displays should be disabled (OFF). When switching from one layout to another, we go
+ * through each of the displays and make sure any displays we might have disabled are
+ * enabled again.
*/
private void resetLayoutLocked() {
final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState);
@@ -481,7 +475,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final Layout.Display displayLayout = layout.getAt(i);
final LogicalDisplay display = getDisplayLocked(displayLayout.getLogicalDisplayId());
if (display != null) {
- enableDisplayLocked(display, false);
+ enableDisplayLocked(display, true); // Reset all displays back to enabled
}
}
}
@@ -503,7 +497,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// If the underlying display-device we want to use for this display
// doesn't exist, then skip it. This can happen at startup as display-devices
- // trickle in one at a time, or if the layout has an error.
+ // trickle in one at a time. When the new display finally shows up, the layout is
+ // recalculated so that the display is properly added to the current layout.
final DisplayAddress address = displayLayout.getAddress();
final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
if (device == null) {
@@ -526,7 +521,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
if (newDisplay != oldDisplay) {
newDisplay.swapDisplaysLocked(oldDisplay);
}
- enableDisplayLocked(newDisplay, true);
+ enableDisplayLocked(newDisplay, displayLayout.isEnabled());
}
}
@@ -545,13 +540,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateLocked(mDisplayDeviceRepo);
mLogicalDisplays.put(displayId, display);
-
- // Internal displays start off disabled. The display is enabled later if it is part of the
- // currently selected display layout.
- final boolean isEnabled = device != null
- && device.getDisplayDeviceInfoLocked().type != Display.TYPE_INTERNAL;
- enableDisplayLocked(display, isEnabled);
-
+ enableDisplayLocked(display, device != null);
return display;
}
@@ -582,7 +571,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final Layout layoutSet = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isDefault = (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
- layoutSet.createDisplayLocked(info.address, isDefault);
+ layoutSet.createDisplayLocked(info.address, isDefault, true /* isEnabled */);
}
private int assignLayerStackLocked(int displayId) {
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index 18f39e6ade1d..ef336674df5d 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -57,7 +57,7 @@ public class Layout {
* @return The new layout.
*/
public Display createDisplayLocked(
- @NonNull DisplayAddress address, boolean isDefault) {
+ @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled) {
if (contains(address)) {
Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
return null;
@@ -74,7 +74,7 @@ public class Layout {
// different layouts, a logical display can be destroyed and later recreated with the
// same logical display ID.
final int logicalDisplayId = assignDisplayIdLocked(isDefault);
- final Display layout = new Display(address, logicalDisplayId);
+ final Display layout = new Display(address, logicalDisplayId, isEnabled);
mDisplays.add(layout);
return layout;
@@ -130,17 +130,25 @@ public class Layout {
* Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s.
*/
public static class Display {
+ // Address of the display device to map to this display.
private final DisplayAddress mAddress;
+
+ // Logical Display ID to apply to this display.
private final int mLogicalDisplayId;
- Display(@NonNull DisplayAddress address, int logicalDisplayId) {
+ // Indicates that this display is not usable and should remain off.
+ private final boolean mIsEnabled;
+
+ Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled) {
mAddress = address;
mLogicalDisplayId = logicalDisplayId;
+ mIsEnabled = isEnabled;
}
@Override
public String toString() {
- return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId + "}";
+ return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId
+ + "(" + (mIsEnabled ? "ON" : "OFF") + ")}";
}
public DisplayAddress getAddress() {
@@ -150,5 +158,9 @@ public class Layout {
public int getLogicalDisplayId() {
return mLogicalDisplayId;
}
+
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8d6bcadb3e2b..18f7a068657f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -666,8 +666,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mSelectRequestBuffer.process();
resetSelectRequestBuffer();
- addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
- addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
+ List<HotplugDetectionAction> hotplugActions
+ = getActions(HotplugDetectionAction.class);
+ if (hotplugActions.isEmpty()) {
+ addAndStartAction(
+ new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+ }
+
+ List<PowerStatusMonitorAction> powerStatusActions
+ = getActions(PowerStatusMonitorAction.class);
+ if (powerStatusActions.isEmpty()) {
+ addAndStartAction(
+ new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
+ }
HdmiDeviceInfo avr = getAvrDeviceInfo();
if (avr != null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 8c404249cfd5..6f7473d60121 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -106,9 +106,7 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_SET_STREAM_PATH, mBystander);
addHandler(Constants.MESSAGE_STANDBY, mBystander);
addHandler(Constants.MESSAGE_SET_MENU_LANGUAGE, mBystander);
- addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBystander);
addHandler(Constants.MESSAGE_USER_CONTROL_RELEASED, mBystander);
- addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBystander);
addHandler(Constants.MESSAGE_FEATURE_ABORT, mBystander);
addHandler(Constants.MESSAGE_INACTIVE_SOURCE, mBystander);
addHandler(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, mBystander);
@@ -133,6 +131,8 @@ public final class HdmiCecStandbyModeHandler {
addHandler(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, mBypasser);
addHandler(Constants.MESSAGE_GIVE_OSD_NAME, mBypasser);
addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
+ addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
+ addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 3cc32bef0e67..851ea3d01085 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -35,7 +35,6 @@ import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.os.Handler;
-import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
@@ -63,7 +62,6 @@ public class LockdownVpnTracker {
@NonNull private final Handler mHandler;
@NonNull private final Vpn mVpn;
@NonNull private final VpnProfile mProfile;
- @NonNull private final KeyStore mKeyStore;
@NonNull private final Object mStateLock = new Object();
@@ -132,7 +130,6 @@ public class LockdownVpnTracker {
public LockdownVpnTracker(@NonNull Context context,
@NonNull Handler handler,
- @NonNull KeyStore keyStore,
@NonNull Vpn vpn,
@NonNull VpnProfile profile) {
mContext = Objects.requireNonNull(context);
@@ -140,7 +137,6 @@ public class LockdownVpnTracker {
mHandler = Objects.requireNonNull(handler);
mVpn = Objects.requireNonNull(vpn);
mProfile = Objects.requireNonNull(profile);
- mKeyStore = Objects.requireNonNull(keyStore);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
@@ -212,7 +208,7 @@ public class LockdownVpnTracker {
// network is the system default. So, if the VPN is up and underlying network
// (e.g., wifi) disconnects, CS will inform apps that the VPN's capabilities have
// changed to match the new default network (e.g., cell).
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, network, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, network, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ebf1fe9d7cd0..e0f534602dde 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -24,7 +24,6 @@ import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
import static android.net.NetworkStack.checkNetworkStackPermission;
@@ -45,6 +44,7 @@ import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
+import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UNSUPPORTED;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 460b2f2bf5c6..903652ab76a5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2086,15 +2086,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
sealLocked();
- // Session that are staged, ready and not multi package will be installed during
- // this boot. As such, we need populate all the fields for successful installation.
- if (isMultiPackage()) {
+ // Session that are staged, committed and not multi package will be installed or
+ // restart verification during this boot. As such, we need populate all the fields
+ // for successful installation.
+ if (isMultiPackage() || !isStaged() || !isCommitted()) {
return;
}
final PackageInstallerSession root = hasParentSessionId()
? allSessions.get(getParentSessionId())
: this;
- if (root != null && root.isStagedSessionReady()) {
+ if (root != null) {
if (isApexSession()) {
validateApexInstallLocked();
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index c9067a3a9392..b61fd8d633f6 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -33,9 +33,9 @@ import android.util.SparseArray;
import com.android.internal.util.CollectionUtils;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import java.util.Arrays;
import java.util.function.Function;
@@ -169,8 +169,8 @@ public class DomainVerificationDebug {
}
ArraySet<String> allWebDomains = mCollector.collectAllWebDomains(pkg);
- SparseArray<DomainVerificationUserState> userStates =
- pkgState.getUserSelectionStates();
+ SparseArray<DomainVerificationInternalUserState> userStates =
+ pkgState.getUserStates();
if (userId == UserHandle.USER_ALL) {
int size = userStates.size();
if (size == 0) {
@@ -178,13 +178,13 @@ public class DomainVerificationDebug {
wasHeaderPrinted);
} else {
for (int index = 0; index < size; index++) {
- DomainVerificationUserState userState = userStates.valueAt(index);
+ DomainVerificationInternalUserState userState = userStates.valueAt(index);
printState(writer, pkgState, userState.getUserId(), userState, reusedSet,
allWebDomains, wasHeaderPrinted);
}
}
} else {
- DomainVerificationUserState userState = userStates.get(userId);
+ DomainVerificationInternalUserState userState = userStates.get(userId);
printState(writer, pkgState, userId, userState, reusedSet, allWebDomains,
wasHeaderPrinted);
}
@@ -192,8 +192,9 @@ public class DomainVerificationDebug {
boolean printState(@NonNull IndentingPrintWriter writer,
@NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
- @Nullable DomainVerificationUserState userState, @NonNull ArraySet<String> reusedSet,
- @NonNull ArraySet<String> allWebDomains, boolean wasHeaderPrinted) {
+ @Nullable DomainVerificationInternalUserState userState,
+ @NonNull ArraySet<String> reusedSet, @NonNull ArraySet<String> allWebDomains,
+ boolean wasHeaderPrinted) {
reusedSet.clear();
reusedSet.addAll(allWebDomains);
if (userState != null) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index ed37fa0da01f..1721a18f4f60 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -132,6 +132,21 @@ public class DomainVerificationEnforcer {
/**
* Enforced when mutating user selection state inside an exposed API method.
*/
+ public boolean assertApprovedUserStateQuerent(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) throws SecurityException {
+ if (callingUserId != targetUserId) {
+ mContext.enforcePermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit other users");
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ /**
+ * Enforced when mutating user selection state inside an exposed API method.
+ */
public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
@Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
if (callingUserId != targetUserId) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index a68b3da0f0b6..0c2b4c547dae 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -48,7 +48,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
-public interface DomainVerificationManagerInternal extends DomainVerificationManager {
+public interface DomainVerificationManagerInternal {
UUID DISABLED_ID = new UUID(0, 0);
@@ -69,8 +69,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* during the legacy transition period.
*
* TODO(b/177923646): The legacy values can be removed once the Settings API changes are
- * shipped. These values are not stable, so just deleting the constant and shifting others is
- * fine.
+ * shipped. These values are not stable, so just deleting the constant and shifting others is
+ * fine.
*/
int APPROVAL_LEVEL_LEGACY_ASK = 1;
@@ -84,14 +84,15 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
/**
* The app has been chosen by the user through
- * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)}, indictag an explicit
- * choice to use this app to open an unverified domain.
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
+ * indicating an explicit choice to use this app to open an unverified domain.
*/
int APPROVAL_LEVEL_SELECTION = 2;
/**
* The app is approved through the digital asset link statement being hosted at the domain
- * it is capturing. This is set through {@link #setDomainVerificationStatus(UUID, Set, int)} by
+ * it is capturing. This is set through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
* the domain verification agent on device.
*/
int APPROVAL_LEVEL_VERIFIED = 3;
@@ -102,7 +103,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* declares against the digital asset link statements before allowing it to be installed.
*
* The user is still able to disable instant app link handling through
- * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+ * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
*/
int APPROVAL_LEVEL_INSTANT_APP = 4;
@@ -122,7 +123,17 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
APPROVAL_LEVEL_VERIFIED,
APPROVAL_LEVEL_INSTANT_APP
})
- @interface ApprovalLevel{}
+ @interface ApprovalLevel {
+ }
+
+ /** @see DomainVerificationManager#getDomainVerificationInfo(String) */
+ @Nullable
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ })
+ DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+ throws NameNotFoundException;
/**
* Generate a new domain set ID to be used for attaching new packages.
@@ -173,9 +184,9 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
/**
* Migrates verification state from a previous install to a new one. It is expected that the
* {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
- * {@link #generateNewId()}. This will preserve {@link #STATE_SUCCESS} domains under the
- * assumption that the new package will pass the same server side config as the previous
- * package, as they have matching signatures.
+ * {@link #generateNewId()}. This will preserve {@link DomainVerificationManager#STATE_SUCCESS}
+ * domains under the assumption that the new package will pass the same server side config as
+ * the previous package, as they have matching signatures.
* <p>
* This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
* lock. This should never be called from within the domain verification classes themselves.
@@ -229,8 +240,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* tag has already been entered.
* <p>
* This is <b>only</b> for restore, and will override package states, ignoring if their {@link
- * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains marked
- * as success verify against the server correctly, although the verification agent may decide to
+ * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains
+ * marked
+ * as success verify against the server correctly, although the verification agent may decide
+ * to
* re-verify them when it gets the chance.
*/
/*
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index 6f2810785c60..a7a52e0cd10c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -23,29 +23,29 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
-import android.content.pm.verify.domain.DomainVerificationManagerImpl;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.os.ServiceSpecificException;
import java.util.List;
import java.util.UUID;
-class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
+public class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
@NonNull
- private DomainVerificationService mService;
+ private final DomainVerificationService mService;
- DomainVerificationManagerStub(DomainVerificationService service) {
+ public DomainVerificationManagerStub(DomainVerificationService service) {
mService = service;
}
@NonNull
@Override
- public List<String> getValidVerificationPackageNames() {
+ public List<String> queryValidVerificationPackageNames() {
try {
- return mService.getValidVerificationPackageNames();
+ return mService.queryValidVerificationPackageNames();
} catch (Exception e) {
throw rethrow(e);
}
@@ -95,10 +95,10 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
@Nullable
@Override
- public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ public DomainVerificationUserState getDomainVerificationUserState(
String packageName, @UserIdInt int userId) {
try {
- return mService.getDomainVerificationUserSelection(packageName, userId);
+ return mService.getDomainVerificationUserState(packageName, userId);
} catch (Exception e) {
throw rethrow(e);
}
@@ -117,13 +117,13 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
private RuntimeException rethrow(Exception exception) throws RuntimeException {
if (exception instanceof InvalidDomainSetException) {
- int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET;
+ int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
return new ServiceSpecificException(packedErrorCode,
((InvalidDomainSetException) exception).getPackageName());
} else if (exception instanceof NameNotFoundException) {
return new ServiceSpecificException(
- DomainVerificationManagerImpl.ERROR_NAME_NOT_FOUND);
+ DomainVerificationManager.ERROR_NAME_NOT_FOUND);
} else if (exception instanceof RuntimeException) {
return (RuntimeException) exception;
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
index c864b2937f6b..abb8d2fb6e1e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
@@ -27,9 +27,9 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.server.pm.SettingsXml;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import org.xmlpull.v1.XmlPullParserException;
@@ -157,7 +157,7 @@ public class DomainVerificationPersistence {
UUID id = UUID.fromString(idString);
final ArrayMap<String, Integer> stateMap = new ArrayMap<>();
- final SparseArray<DomainVerificationUserState> userStates = new SparseArray<>();
+ final SparseArray<DomainVerificationInternalUserState> userStates = new SparseArray<>();
SettingsXml.ChildSection child = section.children();
while (child.moveToNext()) {
@@ -176,10 +176,10 @@ public class DomainVerificationPersistence {
}
private static void readUserStates(@NonNull SettingsXml.ReadSection section,
- @NonNull SparseArray<DomainVerificationUserState> userStates) {
+ @NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
SettingsXml.ChildSection child = section.children();
while (child.moveToNext(TAG_USER_STATE)) {
- DomainVerificationUserState userState = createUserStateFromXml(child);
+ DomainVerificationInternalUserState userState = createUserStateFromXml(child);
if (userState != null) {
userStates.put(userState.getUserId(), userState);
}
@@ -205,12 +205,12 @@ public class DomainVerificationPersistence {
.attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS,
pkgState.isHasAutoVerifyDomains())) {
writeStateMap(parentSection, pkgState.getStateMap());
- writeUserStates(parentSection, pkgState.getUserSelectionStates());
+ writeUserStates(parentSection, pkgState.getUserStates());
}
}
private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection,
- @NonNull SparseArray<DomainVerificationUserState> states) throws IOException {
+ @NonNull SparseArray<DomainVerificationInternalUserState> states) throws IOException {
int size = states.size();
if (size == 0) {
return;
@@ -245,7 +245,7 @@ public class DomainVerificationPersistence {
* entered.
*/
@Nullable
- public static DomainVerificationUserState createUserStateFromXml(
+ public static DomainVerificationInternalUserState createUserStateFromXml(
@NonNull SettingsXml.ReadSection section) {
int userId = section.getInt(ATTR_USER_ID);
if (userId == -1) {
@@ -260,7 +260,7 @@ public class DomainVerificationPersistence {
readEnabledHosts(child, enabledHosts);
}
- return new DomainVerificationUserState(userId, enabledHosts, allowLinkHandling);
+ return new DomainVerificationInternalUserState(userId, enabledHosts, allowLinkHandling);
}
private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section,
@@ -275,7 +275,7 @@ public class DomainVerificationPersistence {
}
public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
- @NonNull DomainVerificationUserState userState) throws IOException {
+ @NonNull DomainVerificationInternalUserState userState) throws IOException {
try (SettingsXml.WriteSection section =
parentSection.startSection(TAG_USER_STATE)
.attribute(ATTR_USER_ID, userState.getUserId())
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index dbd7f96f2cbc..e85bbe41f747 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -34,8 +34,9 @@ import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationState;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -55,9 +56,9 @@ import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
@@ -208,8 +209,7 @@ public class DomainVerificationService extends SystemService
}
@NonNull
- @Override
- public List<String> getValidVerificationPackageNames() {
+ public List<String> queryValidVerificationPackageNames() {
mEnforcer.assertApprovedVerifier(mConnection.getCallingUid(), mProxy);
List<String> packageNames = new ArrayList<>();
synchronized (mLock) {
@@ -272,7 +272,6 @@ public class DomainVerificationService extends SystemService
}
}
- @Override
public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
int state) throws InvalidDomainSetException, NameNotFoundException {
if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
@@ -314,7 +313,7 @@ public class DomainVerificationService extends SystemService
int size = verifiedDomains.size();
for (int index = 0; index < size; index++) {
- removeUserSelectionsForDomain(verifiedDomains.get(index));
+ removeUserStatesForDomain(verifiedDomains.get(index));
}
}
@@ -401,12 +400,12 @@ public class DomainVerificationService extends SystemService
}
}
- private void removeUserSelectionsForDomain(@NonNull String domain) {
+ private void removeUserStatesForDomain(@NonNull String domain) {
synchronized (mLock) {
final int size = mAttachedPkgStates.size();
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
- SparseArray<DomainVerificationUserState> array = pkgState.getUserSelectionStates();
+ SparseArray<DomainVerificationInternalUserState> array = pkgState.getUserStates();
int arraySize = array.size();
for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
array.valueAt(arrayIndex).removeHost(domain);
@@ -415,13 +414,6 @@ public class DomainVerificationService extends SystemService
}
}
- @Override
- public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
- boolean allowed) throws NameNotFoundException {
- setDomainVerificationLinkHandlingAllowed(packageName, allowed,
- mConnection.getCallingUserId());
- }
-
public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
@@ -434,7 +426,7 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserSelectionState(userId)
+ pkgState.getOrCreateUserState(userId)
.setLinkHandlingAllowed(allowed);
}
@@ -452,11 +444,11 @@ public class DomainVerificationService extends SystemService
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
if (userId == UserHandle.USER_ALL) {
for (int aUserId : mConnection.getAllUserIds()) {
- pkgState.getOrCreateUserSelectionState(aUserId)
+ pkgState.getOrCreateUserState(aUserId)
.setLinkHandlingAllowed(allowed);
}
} else {
- pkgState.getOrCreateUserSelectionState(userId)
+ pkgState.getOrCreateUserState(userId)
.setLinkHandlingAllowed(allowed);
}
}
@@ -468,7 +460,7 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserSelectionState(userId)
+ pkgState.getOrCreateUserState(userId)
.setLinkHandlingAllowed(allowed);
}
}
@@ -476,14 +468,6 @@ public class DomainVerificationService extends SystemService
mConnection.scheduleWriteSettings();
}
- @Override
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean enabled)
- throws InvalidDomainSetException, NameNotFoundException {
- setDomainVerificationUserSelection(domainSetId, domains, enabled,
- mConnection.getCallingUserId());
- }
-
public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
throws InvalidDomainSetException, NameNotFoundException {
@@ -500,7 +484,8 @@ public class DomainVerificationService extends SystemService
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
false /* forAutoVerify */, callingUid, userId);
- DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+ DomainVerificationInternalUserState userState =
+ pkgState.getOrCreateUserState(userId);
// Disable other packages if approving this one. Note that this check is only done for
// enabling. This allows an escape hatch in case multiple packages somehow get selected.
@@ -540,8 +525,8 @@ public class DomainVerificationService extends SystemService
continue;
}
- DomainVerificationUserState approvedUserState =
- approvedPkgState.getUserSelectionState(userId);
+ DomainVerificationInternalUserState approvedUserState =
+ approvedPkgState.getUserState(userId);
if (approvedUserState == null) {
continue;
}
@@ -623,8 +608,8 @@ public class DomainVerificationService extends SystemService
if (userId == UserHandle.USER_ALL) {
for (int aUserId : mConnection.getAllUserIds()) {
- DomainVerificationUserState userState =
- pkgState.getOrCreateUserSelectionState(aUserId);
+ DomainVerificationInternalUserState userState =
+ pkgState.getOrCreateUserState(aUserId);
if (enabled) {
userState.addHosts(domains);
} else {
@@ -632,7 +617,8 @@ public class DomainVerificationService extends SystemService
}
}
} else {
- DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+ DomainVerificationInternalUserState userState =
+ pkgState.getOrCreateUserState(userId);
if (enabled) {
userState.addHosts(domains);
} else {
@@ -643,17 +629,9 @@ public class DomainVerificationService extends SystemService
@Nullable
@Override
- public DomainVerificationUserSelection getDomainVerificationUserSelection(
- @NonNull String packageName) throws NameNotFoundException {
- return getDomainVerificationUserSelection(packageName,
- mConnection.getCallingUserId());
- }
-
- @Nullable
- @Override
- public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ public DomainVerificationUserState getDomainVerificationUserState(
@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
- if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ if (!mEnforcer.assertApprovedUserStateQuerent(mConnection.getCallingUid(),
mConnection.getCallingUserId(), packageName, userId)) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
@@ -673,7 +651,7 @@ public class DomainVerificationService extends SystemService
Map<String, Integer> domains = new ArrayMap<>(webDomainsSize);
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
- DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+ DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
Set<String> enabledHosts = userState == null ? emptySet() : userState.getEnabledHosts();
for (int index = 0; index < webDomainsSize; index++) {
@@ -682,11 +660,11 @@ public class DomainVerificationService extends SystemService
int domainState;
if (state != null && DomainVerificationManager.isStateVerified(state)) {
- domainState = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED;
+ domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
} else if (enabledHosts.contains(host)) {
- domainState = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED;
+ domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
} else {
- domainState = DomainVerificationUserSelection.DOMAIN_STATE_NONE;
+ domainState = DomainVerificationUserState.DOMAIN_STATE_NONE;
}
domains.put(host, domainState);
@@ -694,17 +672,11 @@ public class DomainVerificationService extends SystemService
boolean linkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed();
- return new DomainVerificationUserSelection(pkgState.getId(), packageName,
+ return new DomainVerificationUserState(pkgState.getId(), packageName,
UserHandle.of(userId), linkHandlingAllowed, domains);
}
}
- @NonNull
- @Override
- public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
- return getOwnersForDomain(domain, mConnection.getCallingUserId());
- }
-
public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) {
mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
userId);
@@ -795,7 +767,7 @@ public class DomainVerificationService extends SystemService
AndroidPackage newPkg = newPkgSetting.getPkg();
ArrayMap<String, Integer> newStateMap = new ArrayMap<>();
- SparseArray<DomainVerificationUserState> newUserStates = new SparseArray<>();
+ SparseArray<DomainVerificationInternalUserState> newUserStates = new SparseArray<>();
if (oldPkgState == null || oldPkg == null || newPkg == null) {
// Should be impossible, but to be safe, continue with a new blank state instead
@@ -838,21 +810,22 @@ public class DomainVerificationService extends SystemService
}
}
- SparseArray<DomainVerificationUserState> oldUserStates =
- oldPkgState.getUserSelectionStates();
+ SparseArray<DomainVerificationInternalUserState> oldUserStates =
+ oldPkgState.getUserStates();
int oldUserStatesSize = oldUserStates.size();
if (oldUserStatesSize > 0) {
ArraySet<String> newWebDomains = mCollector.collectValidAutoVerifyDomains(newPkg);
for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
oldUserStatesIndex++) {
int userId = oldUserStates.keyAt(oldUserStatesIndex);
- DomainVerificationUserState oldUserState = oldUserStates.valueAt(
+ DomainVerificationInternalUserState oldUserState = oldUserStates.valueAt(
oldUserStatesIndex);
ArraySet<String> oldEnabledHosts = oldUserState.getEnabledHosts();
ArraySet<String> newEnabledHosts = new ArraySet<>(oldEnabledHosts);
newEnabledHosts.retainAll(newWebDomains);
- DomainVerificationUserState newUserState = new DomainVerificationUserState(
- userId, newEnabledHosts, oldUserState.isLinkHandlingAllowed());
+ DomainVerificationInternalUserState newUserState =
+ new DomainVerificationInternalUserState(userId, newEnabledHosts,
+ oldUserState.isLinkHandlingAllowed());
newUserStates.put(userId, newUserState);
}
}
@@ -926,7 +899,7 @@ public class DomainVerificationService extends SystemService
webDomains = mCollector.collectAllWebDomains(pkg);
}
- pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains);
+ pkgState.getOrCreateUserState(userId).addHosts(webDomains);
}
}
@@ -1295,7 +1268,7 @@ public class DomainVerificationService extends SystemService
}
@Override
- public void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId) {
+ public void clearUserStates(@Nullable List<String> packageNames, @UserIdInt int userId) {
mEnforcer.assertInternal(mConnection.getCallingUid());
synchronized (mLock) {
if (packageNames == null) {
@@ -1545,7 +1518,7 @@ public class DomainVerificationService extends SystemService
return APPROVAL_LEVEL_NONE;
}
- DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+ DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
if (userState != null && !userState.isLinkHandlingAllowed()) {
if (DEBUG_APPROVAL) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index a8e937cf2b90..f3d1dbb1f6ad 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -29,9 +29,9 @@ import android.util.TypedXmlSerializer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import org.xmlpull.v1.XmlPullParserException;
@@ -216,21 +216,22 @@ class DomainVerificationSettings {
}
}
- SparseArray<DomainVerificationUserState> oldSelectionStates =
- oldState.getUserSelectionStates();
+ SparseArray<DomainVerificationInternalUserState> oldSelectionStates =
+ oldState.getUserStates();
- SparseArray<DomainVerificationUserState> newSelectionStates =
- newState.getUserSelectionStates();
+ SparseArray<DomainVerificationInternalUserState> newSelectionStates =
+ newState.getUserStates();
- DomainVerificationUserState newUserState = newSelectionStates.get(UserHandle.USER_SYSTEM);
+ DomainVerificationInternalUserState newUserState =
+ newSelectionStates.get(UserHandle.USER_SYSTEM);
if (newUserState != null) {
ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
- DomainVerificationUserState oldUserState =
+ DomainVerificationInternalUserState oldUserState =
oldSelectionStates.get(UserHandle.USER_SYSTEM);
boolean linkHandlingAllowed = newUserState.isLinkHandlingAllowed();
if (oldUserState == null) {
- oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM,
+ oldUserState = new DomainVerificationInternalUserState(UserHandle.USER_SYSTEM,
newEnabledHosts, linkHandlingAllowed);
oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index d083d11cb2e2..94767f555574 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -24,7 +24,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
import android.os.Binder;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -118,7 +118,7 @@ public class DomainVerificationShell {
case "set-app-links":
return runSetAppLinks(commandHandler);
case "set-app-links-user-selection":
- return runSetAppLinksUserSelection(commandHandler);
+ return runSetAppLinksUserState(commandHandler);
case "set-app-links-allowed":
return runSetAppLinksAllowed(commandHandler);
}
@@ -193,7 +193,7 @@ public class DomainVerificationShell {
}
// pm set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>] <ENABLED> <DOMAINS>...
- private boolean runSetAppLinksUserSelection(@NonNull BasicShellCommandHandler commandHandler) {
+ private boolean runSetAppLinksUserState(@NonNull BasicShellCommandHandler commandHandler) {
Integer userId = null;
String packageName = null;
@@ -224,7 +224,7 @@ public class DomainVerificationShell {
return false;
}
- userId = translateUserId(userId, "runSetAppLinksUserSelection");
+ userId = translateUserId(userId, "runSetAppLinksUserState");
String enabledString = commandHandler.getNextArgRequired();
@@ -326,7 +326,7 @@ public class DomainVerificationShell {
}
if (userId != null) {
- mCallback.clearUserSelections(packageNames, userId);
+ mCallback.clearUserStates(packageNames, userId);
} else {
mCallback.clearDomainVerificationState(packageNames);
}
@@ -457,10 +457,10 @@ public class DomainVerificationShell {
throws PackageManager.NameNotFoundException;
/**
- * @see DomainVerificationManager#getDomainVerificationUserSelection(String)
+ * @see DomainVerificationManager#getDomainVerificationUserState(String)
*/
@Nullable
- DomainVerificationUserSelection getDomainVerificationUserSelection(
+ DomainVerificationUserState getDomainVerificationUserState(
@NonNull String packageName, @UserIdInt int userId)
throws PackageManager.NameNotFoundException;
@@ -486,7 +486,7 @@ public class DomainVerificationShell {
* Reset all the user selections for the given package names, or all package names if null
* is provided.
*/
- void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId);
+ void clearUserStates(@Nullable List<String> packageNames, @UserIdInt int userId);
/**
* Broadcast a verification request for the given package names, or all package names if
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
index 8fbb33afb6ca..aa7407ce3fe8 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
@@ -29,7 +29,7 @@ import java.util.Set;
* when a web URL Intent is sent and the application is the highest priority for that domain.
*/
@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true, genBuilder = false)
-public class DomainVerificationUserState {
+public class DomainVerificationInternalUserState {
@UserIdInt
private final int mUserId;
@@ -43,32 +43,32 @@ public class DomainVerificationUserState {
*/
private boolean mLinkHandlingAllowed = true;
- public DomainVerificationUserState(@UserIdInt int userId) {
+ public DomainVerificationInternalUserState(@UserIdInt int userId) {
mUserId = userId;
mEnabledHosts = new ArraySet<>();
}
- public DomainVerificationUserState addHosts(@NonNull ArraySet<String> newHosts) {
+ public DomainVerificationInternalUserState addHosts(@NonNull ArraySet<String> newHosts) {
mEnabledHosts.addAll(newHosts);
return this;
}
- public DomainVerificationUserState addHosts(@NonNull Set<String> newHosts) {
+ public DomainVerificationInternalUserState addHosts(@NonNull Set<String> newHosts) {
mEnabledHosts.addAll(newHosts);
return this;
}
- public DomainVerificationUserState removeHost(String host) {
+ public DomainVerificationInternalUserState removeHost(String host) {
mEnabledHosts.remove(host);
return this;
}
- public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
+ public DomainVerificationInternalUserState removeHosts(@NonNull ArraySet<String> newHosts) {
mEnabledHosts.removeAll(newHosts);
return this;
}
- public DomainVerificationUserState removeHosts(@NonNull Set<String> newHosts) {
+ public DomainVerificationInternalUserState removeHosts(@NonNull Set<String> newHosts) {
mEnabledHosts.removeAll(newHosts);
return this;
}
@@ -81,8 +81,7 @@ public class DomainVerificationUserState {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm
- // /verify/domain/models/DomainVerificationUserState.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -90,7 +89,7 @@ public class DomainVerificationUserState {
/**
- * Creates a new DomainVerificationUserState.
+ * Creates a new DomainVerificationInternalUserState.
*
* @param enabledHosts
* List of domains which have been enabled by the user. *
@@ -98,7 +97,7 @@ public class DomainVerificationUserState {
* Whether to allow this package to automatically open links by auto verification.
*/
@DataClass.Generated.Member
- public DomainVerificationUserState(
+ public DomainVerificationInternalUserState(
@UserIdInt int userId,
@NonNull ArraySet<String> enabledHosts,
boolean linkHandlingAllowed) {
@@ -138,7 +137,7 @@ public class DomainVerificationUserState {
* Whether to allow this package to automatically open links by auto verification.
*/
@DataClass.Generated.Member
- public @NonNull DomainVerificationUserState setLinkHandlingAllowed( boolean value) {
+ public @NonNull DomainVerificationInternalUserState setLinkHandlingAllowed( boolean value) {
mLinkHandlingAllowed = value;
return this;
}
@@ -149,7 +148,7 @@ public class DomainVerificationUserState {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
- return "DomainVerificationUserState { " +
+ return "DomainVerificationInternalUserState { " +
"userId = " + mUserId + ", " +
"enabledHosts = " + mEnabledHosts + ", " +
"linkHandlingAllowed = " + mLinkHandlingAllowed +
@@ -160,13 +159,13 @@ public class DomainVerificationUserState {
@DataClass.Generated.Member
public boolean equals(@android.annotation.Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
+ // boolean fieldNameEquals(DomainVerificationInternalUserState other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
- DomainVerificationUserState that = (DomainVerificationUserState) o;
+ DomainVerificationInternalUserState that = (DomainVerificationInternalUserState) o;
//noinspection PointlessBooleanExpression
return true
&& mUserId == that.mUserId
@@ -188,10 +187,10 @@ public class DomainVerificationUserState {
}
@DataClass.Generated(
- time = 1612894390039L,
+ time = 1614714563905L,
codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java",
- inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate boolean mLinkHandlingAllowed\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true, genBuilder=false)")
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java",
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate boolean mLinkHandlingAllowed\npublic com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState addHosts(java.util.Set<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHost(java.lang.String)\npublic com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationInternalUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index 48099aa5382b..a089a6022735 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -19,7 +19,6 @@ package com.android.server.pm.verify.domain.models;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -44,9 +43,9 @@ public class DomainVerificationPkgState {
/**
* Whether or not the package declares any autoVerify domains. This is separate from an empty
- * check on the map itself, because an empty map means no response recorded, not necessarily no
- * domains declared. When this is false, {@link #mStateMap} will be empty, but
- * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to
+ * check on the map itself, because an empty map means no response recorded, not necessarily
+ * no domains declared. When this is false, {@link #mStateMap} will be empty, but
+ * {@link #mUserStates} may contain any domains the user has explicitly chosen to
* allow this package to open, which may or may not be marked autoVerify.
*/
private final boolean mHasAutoVerifyDomains;
@@ -62,7 +61,7 @@ public class DomainVerificationPkgState {
private final ArrayMap<String, Integer> mStateMap;
@NonNull
- private final SparseArray<DomainVerificationUserState> mUserSelectionStates;
+ private final SparseArray<DomainVerificationInternalUserState> mUserStates;
public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
boolean hasAutoVerifyDomains) {
@@ -70,16 +69,17 @@ public class DomainVerificationPkgState {
}
@Nullable
- public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) {
- return mUserSelectionStates.get(userId);
+ public DomainVerificationInternalUserState getUserState(@UserIdInt int userId) {
+ return mUserStates.get(userId);
}
@Nullable
- public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) {
- DomainVerificationUserState userState = mUserSelectionStates.get(userId);
+ public DomainVerificationInternalUserState getOrCreateUserState(
+ @UserIdInt int userId) {
+ DomainVerificationInternalUserState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new DomainVerificationUserState(userId);
- mUserSelectionStates.put(userId, userState);
+ userState = new DomainVerificationInternalUserState(userId);
+ mUserStates.put(userId, userState);
}
return userState;
}
@@ -89,20 +89,20 @@ public class DomainVerificationPkgState {
}
public void removeUser(@UserIdInt int userId) {
- mUserSelectionStates.remove(userId);
+ mUserStates.remove(userId);
}
public void removeAllUsers() {
- mUserSelectionStates.clear();
+ mUserStates.clear();
}
- private int userSelectionStatesHashCode() {
- return mUserSelectionStates.contentHashCode();
+ private int userStatesHashCode() {
+ return mUserStates.contentHashCode();
}
- private boolean userSelectionStatesEquals(
- @NonNull SparseArray<DomainVerificationUserState> other) {
- return mUserSelectionStates.contentEquals(other);
+ private boolean userStatesEquals(
+ @NonNull SparseArray<DomainVerificationInternalUserState> other) {
+ return mUserStates.contentEquals(other);
}
@@ -113,7 +113,7 @@ public class DomainVerificationPkgState {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -123,9 +123,15 @@ public class DomainVerificationPkgState {
/**
* Creates a new DomainVerificationPkgState.
*
+ * @param hasAutoVerifyDomains
+ * Whether or not the package declares any autoVerify domains. This is separate from an empty
+ * check on the map itself, because an empty map means no response recorded, not necessarily
+ * no domains declared. When this is false, {@link #mStateMap} will be empty, but
+ * {@link #mUserStates} may contain any domains the user has explicitly chosen to
+ * allow this package to open, which may or may not be marked autoVerify.
* @param stateMap
* Map of domains to state integers. Only domains that are not set to the default value of
- * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+ * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
*
* TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
* such as storing no state when the package is marked as a linked app in SystemConfig.
@@ -136,7 +142,7 @@ public class DomainVerificationPkgState {
@NonNull UUID id,
boolean hasAutoVerifyDomains,
@NonNull ArrayMap<String,Integer> stateMap,
- @NonNull SparseArray<DomainVerificationUserState> userSelectionStates) {
+ @NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
this.mPackageName = packageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
@@ -147,9 +153,9 @@ public class DomainVerificationPkgState {
this.mStateMap = stateMap;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mStateMap);
- this.mUserSelectionStates = userSelectionStates;
+ this.mUserStates = userStates;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mUserSelectionStates);
+ NonNull.class, null, mUserStates);
// onConstructed(); // You can define this method to get a callback
}
@@ -164,6 +170,13 @@ public class DomainVerificationPkgState {
return mId;
}
+ /**
+ * Whether or not the package declares any autoVerify domains. This is separate from an empty
+ * check on the map itself, because an empty map means no response recorded, not necessarily
+ * no domains declared. When this is false, {@link #mStateMap} will be empty, but
+ * {@link #mUserStates} may contain any domains the user has explicitly chosen to
+ * allow this package to open, which may or may not be marked autoVerify.
+ */
@DataClass.Generated.Member
public boolean isHasAutoVerifyDomains() {
return mHasAutoVerifyDomains;
@@ -171,7 +184,7 @@ public class DomainVerificationPkgState {
/**
* Map of domains to state integers. Only domains that are not set to the default value of
- * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+ * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
*
* TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
* such as storing no state when the package is marked as a linked app in SystemConfig.
@@ -182,8 +195,8 @@ public class DomainVerificationPkgState {
}
@DataClass.Generated.Member
- public @NonNull SparseArray<DomainVerificationUserState> getUserSelectionStates() {
- return mUserSelectionStates;
+ public @NonNull SparseArray<DomainVerificationInternalUserState> getUserStates() {
+ return mUserStates;
}
@Override
@@ -197,7 +210,7 @@ public class DomainVerificationPkgState {
"id = " + mId + ", " +
"hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
"stateMap = " + mStateMap + ", " +
- "userSelectionStates = " + mUserSelectionStates +
+ "userStates = " + mUserStates +
" }";
}
@@ -218,7 +231,7 @@ public class DomainVerificationPkgState {
&& Objects.equals(mId, that.mId)
&& mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
&& Objects.equals(mStateMap, that.mStateMap)
- && userSelectionStatesEquals(that.mUserSelectionStates);
+ && userStatesEquals(that.mUserStates);
}
@Override
@@ -232,15 +245,15 @@ public class DomainVerificationPkgState {
_hash = 31 * _hash + Objects.hashCode(mId);
_hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
_hash = 31 * _hash + Objects.hashCode(mStateMap);
- _hash = 31 * _hash + userSelectionStatesHashCode();
+ _hash = 31 * _hash + userStatesHashCode();
return _hash;
}
@DataClass.Generated(
- time = 1608234185474L,
+ time = 1614818362549L,
codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState> mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userSelectionStatesHashCode()\nprivate boolean userSelectionStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> mUserStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userStatesHashCode()\nprivate boolean userStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index de8823c4b7f3..6366280e1762 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -39,6 +39,7 @@ import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.HidlMemory;
import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
@@ -199,18 +200,7 @@ class ConversionUtil {
hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type);
hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid);
hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid);
-
- // Extract a dup of the underlying FileDescriptor out of aidlModel.data without changing
- // the original.
- FileDescriptor fd = new FileDescriptor();
- try {
- ParcelFileDescriptor dup = aidlModel.data.dup();
- fd.setInt$(dup.detachFd());
- hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(fd, aidlModel.dataSize);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
+ hidlModel.data = parcelFileDescriptorToHidlMemory(aidlModel.data, aidlModel.dataSize);
return hidlModel;
}
@@ -425,4 +415,31 @@ class ConversionUtil {
}
return aidlCapabilities;
}
+
+ /**
+ * Convert an AIDL representation of a shared memory block (ParcelFileDescriptor + size) to the
+ * HIDL representation (HidlMemory). Will not change the incoming data or any ownership
+ * semantics, but rather duplicate the underlying FD.
+ *
+ * @param data The incoming memory block. May be null if dataSize is 0.
+ * @param dataSize The number of bytes in the block.
+ * @return A HidlMemory representation of the memory block. Will be non-null even for an empty
+ * block.
+ */
+ private static @NonNull
+ HidlMemory parcelFileDescriptorToHidlMemory(@Nullable ParcelFileDescriptor data, int dataSize) {
+ if (dataSize > 0) {
+ // Extract a dup of the underlying FileDescriptor out of data.
+ FileDescriptor fd = new FileDescriptor();
+ try {
+ ParcelFileDescriptor dup = data.dup();
+ fd.setInt$(dup.detachFd());
+ return HidlMemoryUtil.fileDescriptorToHidlMemory(fd, dataSize);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ return HidlMemoryUtil.fileDescriptorToHidlMemory(null, 0);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index 43047d1ebaaa..e05c468186ed 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -62,7 +62,9 @@ public class ValidationUtil {
}
validateUuid(model.uuid);
validateUuid(model.vendorUuid);
- Objects.requireNonNull(model.data);
+ if (model.dataSize > 0) {
+ Objects.requireNonNull(model.data);
+ }
}
static void validatePhraseModel(@Nullable PhraseSoundModel model) {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 6aa7c8a290c1..7ed7a592a972 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3759,13 +3759,15 @@ public class StatsPullAtomService extends SystemService {
ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
- FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED,
+ network.getNetId(),
FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
}
@Override
public void onLost(Network network) {
- FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED,
+ network.getNetId(),
FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
}
}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index d2b05c0914d7..9409eb5d1ad9 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -53,11 +53,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService}
@@ -72,6 +74,7 @@ public final class StorageUserConnection {
private final Context mContext;
private final int mUserId;
private final StorageSessionController mSessionController;
+ private final StorageManagerInternal mSmInternal;
private final ActiveConnection mActiveConnection = new ActiveConnection();
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
@GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>();
@@ -81,6 +84,7 @@ public final class StorageUserConnection {
mContext = Objects.requireNonNull(context);
mUserId = Preconditions.checkArgumentNonnegative(userId);
mSessionController = controller;
+ mSmInternal = LocalServices.getService(StorageManagerInternal.class);
mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
mHandlerThread.start();
}
@@ -152,9 +156,13 @@ public final class StorageUserConnection {
*/
public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
throws ExternalStorageServiceException {
+ List<String> primarySessionIds = mSmInternal.getPrimaryVolumeIds();
synchronized (mSessionsLock) {
for (String sessionId : mSessions.keySet()) {
- mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+ if (primarySessionIds.contains(sessionId)) {
+ mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+ return;
+ }
}
}
}
@@ -201,8 +209,7 @@ public final class StorageUserConnection {
return;
}
}
- StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
- sm.resetUser(mUserId);
+ mSmInternal.resetUser(mUserId);
}
/**
@@ -317,6 +324,23 @@ public final class StorageUserConnection {
}
}
+ private void asyncBestEffort(Consumer<IExternalStorageService> consumer) {
+ synchronized (mLock) {
+ if (mRemoteFuture == null) {
+ Slog.w(TAG, "Dropping async request service is not bound");
+ return;
+ }
+
+ IExternalStorageService service = mRemoteFuture.getNow(null);
+ if (service == null) {
+ Slog.w(TAG, "Dropping async request service is not connected");
+ return;
+ }
+
+ consumer.accept(service);
+ }
+ }
+
private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<Void> opFuture = new CompletableFuture<>();
RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
@@ -401,13 +425,13 @@ public final class StorageUserConnection {
public void notifyAnrDelayStarted(String packgeName, int uid, int tid, int reason)
throws ExternalStorageServiceException {
- try {
- waitForAsyncVoid((service, callback) ->
- service.notifyAnrDelayStarted(packgeName, uid, tid, reason, callback));
- } catch (Exception e) {
- throw new ExternalStorageServiceException("Failed to notify ANR delay started: "
- + packgeName, e);
- }
+ asyncBestEffort(service -> {
+ try {
+ service.notifyAnrDelayStarted(packgeName, uid, tid, reason);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify ANR delay started", e);
+ }
+ });
}
private void setResult(Bundle result, CompletableFuture<Void> future) {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 607da3ce6fe2..e84ee672bf0f 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -120,7 +120,9 @@ final class Vibration {
if (newEffect.equals(mEffect)) {
return;
}
- mOriginalEffect = mEffect;
+ if (mOriginalEffect == null) {
+ mOriginalEffect = mEffect;
+ }
mEffect = newEffect;
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 90a763c260f6..c9751bb7abe4 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -344,10 +344,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (!isEffectValid(effect)) {
return;
}
- effect = fixupVibrationEffect(effect);
attrs = fixupVibrationAttributes(attrs);
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
uid, opPkg, reason);
+ // Update with fixed up effect to keep the original effect in Vibration for debugging.
+ vib.updateEffect(fixupVibrationEffect(effect));
synchronized (mLock) {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
@@ -1138,6 +1139,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
void dumpText(PrintWriter pw) {
pw.println("Vibrator Manager Service:");
synchronized (mLock) {
+ pw.println(" mVibrationSettings:");
+ pw.println(" " + mVibrationSettings);
+ pw.println();
pw.println(" mVibratorControllers:");
for (int i = 0; i < mVibrators.size(); i++) {
pw.println(" " + mVibrators.valueAt(i));
@@ -1146,14 +1150,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" mCurrentVibration:");
pw.println(" " + (mCurrentVibration == null
? null : mCurrentVibration.getVibration().getDebugInfo()));
+ pw.println();
pw.println(" mNextVibration:");
pw.println(" " + (mNextVibration == null
? null : mNextVibration.getVibration().getDebugInfo()));
+ pw.println();
pw.println(" mCurrentExternalVibration:");
pw.println(" " + (mCurrentExternalVibration == null
? null : mCurrentExternalVibration.getDebugInfo()));
pw.println();
- pw.println(" mVibrationSettings=" + mVibrationSettings);
for (int i = 0; i < mPreviousVibrations.size(); i++) {
pw.println();
pw.print(" Previous vibrations for usage ");
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 3bbc81a696e6..e6d37b60882e 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1861,7 +1861,7 @@ final class AccessibilityController {
}
@Override
- public boolean isEnabled() {
+ public boolean isAccessibilityTracingEnabled() {
return mTracing.isEnabled();
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dd5a19669f74..55625809b632 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -214,6 +214,7 @@ import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SO
import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -580,9 +581,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private Task mLastParent;
- // Have we told the window clients to show themselves?
- private boolean mClientVisible;
-
boolean firstWindowDrawn;
/** Whether the visible window(s) of this activity is drawn. */
private boolean mReportedDrawn;
@@ -998,7 +996,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(prefix); pw.print("mOrientation=");
pw.println(ActivityInfo.screenOrientationToString(mOrientation));
pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
- + " mVisible=" + mVisible + " mClientVisible=" + mClientVisible
+ + " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ " reportedDrawn=" + mReportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
@@ -1080,6 +1078,46 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
}
+
+ dumpLetterboxInfo(pw, prefix);
+ }
+
+ private void dumpLetterboxInfo(PrintWriter pw, String prefix) {
+ final WindowState mainWin = findMainWindow();
+ if (mainWin == null) {
+ return;
+ }
+
+ boolean isLetterboxed = isLetterboxed(mainWin);
+ pw.println(prefix + "isLetterboxed=" + isLetterboxed);
+ if (!isLetterboxed) {
+ return;
+ }
+
+ pw.println(prefix + " letterboxReason=" + getLetterboxReasonString(mainWin));
+ pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
+ getLetterboxBackgroundColor().toArgb()));
+ pw.println(prefix + " letterboxBackgroundType="
+ + letterboxBackgroundTypeToString(mWmService.getLetterboxBackgroundType()));
+ pw.println(prefix + " letterboxAspectRatio="
+ + computeAspectRatio(getBounds()));
+ }
+
+ /**
+ * Returns a string representing the reason for letterboxing. This method assumes the activity
+ * is letterboxed.
+ */
+ private String getLetterboxReasonString(WindowState mainWin) {
+ if (inSizeCompatMode()) {
+ return "SIZE_COMPAT_MODE";
+ }
+ if (isLetterboxedForFixedOrientationAndAspectRatio()) {
+ return "FIXED_ORIENTATION";
+ }
+ if (mainWin.isLetterboxedForDisplayCutout()) {
+ return "DISPLAY_CUTOUT";
+ }
+ return "UNKNOWN_REASON";
}
void setAppTimeTracker(AppTimeTracker att) {
@@ -1695,7 +1733,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
keysPaused = false;
inHistory = false;
nowVisible = false;
- mClientVisible = true;
+ super.setClientVisible(true);
idle = false;
hasBeenLaunched = false;
mTaskSupervisor = supervisor;
@@ -3735,7 +3773,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
setVisibleRequested(true);
mVisibleSetFromTransferredStartingWindow = true;
}
- setClientVisible(fromActivity.mClientVisible);
+ setClientVisible(fromActivity.isClientVisible());
if (fromActivity.isAnimating()) {
transferAnimation(fromActivity);
@@ -4383,6 +4421,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mVisibleRequested = visible;
+ setInsetsFrozen(!visible);
if (app != null) {
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
}
@@ -5835,18 +5874,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mReportedDrawn;
}
- boolean isClientVisible() {
- return mClientVisible;
- }
-
+ @Override
void setClientVisible(boolean clientVisible) {
- if (mClientVisible == clientVisible || (!clientVisible && mDeferHidingClient)) {
- return;
- }
+ // TODO(shell-transitions): Remove mDeferHidingClient once everything is shell-transitions.
+ // pip activities should just remain in clientVisible.
+ if (!clientVisible && mDeferHidingClient) return;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
Debug.getCallers(5));
- mClientVisible = clientVisible;
+ super.setClientVisible(clientVisible);
sendAppVisibilityToClients();
}
@@ -7390,8 +7426,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int containingAppWidth = containingAppBounds.width();
final int containingAppHeight = containingAppBounds.height();
- final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
- / (float) Math.min(containingAppWidth, containingAppHeight);
+ final float containingRatio = computeAspectRatio(containingAppBounds);
int activityWidth = containingAppWidth;
int activityHeight = containingAppHeight;
@@ -7455,6 +7490,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * Returns the aspect ratio of the given {@code rect}.
+ */
+ private static float computeAspectRatio(Rect rect) {
+ final int width = rect.width();
+ final int height = rect.height();
+ if (width == 0 || height == 0) {
+ return 0;
+ }
+ return Math.max(width, height) / (float) Math.min(width, height);
+ }
+
+ /**
* @return {@code true} if this activity was reparented to another display but
* {@link #ensureActivityConfiguration} is not called.
*/
@@ -8133,7 +8180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(TRANSLUCENT, !occludesParent());
proto.write(VISIBLE, mVisible);
proto.write(VISIBLE_REQUESTED, mVisibleRequested);
- proto.write(CLIENT_VISIBLE, mClientVisible);
+ proto.write(CLIENT_VISIBLE, isClientVisible());
proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
proto.write(REPORTED_DRAWN, mReportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 759b7fe054bc..5ccf576e1099 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,6 +495,21 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return info;
}
+ /**
+ * Gets the stable bounds of the DisplayArea, which is the bounds excluding insets for
+ * navigation bar, cutout, and status bar.
+ */
+ void getStableRect(Rect out) {
+ if (mDisplayContent == null) {
+ getBounds(out);
+ return;
+ }
+
+ // Intersect with the display stable bounds to get the DisplayArea stable bounds.
+ mDisplayContent.getStableRect(out);
+ out.intersect(getBounds());
+ }
+
@Override
public boolean providesMaxBounds() {
return true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9d5c5bed0419..168ab43f4a9a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2677,6 +2677,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
}
+ @Override
void getStableRect(Rect out) {
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
out.set(state.getDisplayFrame());
@@ -3699,8 +3700,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// app.
assignWindowLayers(true /* setLayoutNeeded */);
// 3. The z-order of IME might have been changed. Update the above insets state.
- mInsetsStateController.updateAboveInsetsState(
- mInputMethodWindow, true /* notifyInsetsChange */);
+ mInsetsStateController.updateAboveInsetsState(mInputMethodWindow,
+ mInsetsStateController.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
// 4. Update the IME control target to apply any inset change and animation.
// 5. Reparent the IME container surface to either the input target app, or the IME window
// parent.
@@ -4101,13 +4102,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) {
- // Unfreeze the insets state of the frozen target when the animation finished if exists.
- final Task task = wc.asTask();
- if (task != null) {
- task.forAllWindows(w -> {
- w.clearFrozenInsetsState();
- }, true /* traverseTopToBottom */);
- }
removeImeSurfaceImmediately();
}
}
@@ -4829,7 +4823,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
mNoAnimationNotifyOnTransitionFinished.clear();
- mWallpaperController.hideDeferredWallpapersIfNeeded();
+ mWallpaperController.hideDeferredWallpapersIfNeededLegacy();
onAppTransitionDone();
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 1f7e1524b702..316c20ba5c47 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -99,6 +99,9 @@ class EnsureActivitiesVisibleHelper {
mTask.forAllActivities(a -> {
setActivityVisibilityState(a, starting, resumeTopActivity);
});
+ if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ }
}
private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 27ef147e2781..28c5a6d9323d 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -410,15 +410,19 @@ final class InputMonitor {
return;
}
+ requestFocus(focusToken, focus.getName());
+ }
+
+ private void requestFocus(IBinder focusToken, String windowName) {
if (focusToken == mInputFocus) {
return;
}
mInputFocus = focusToken;
- mInputTransaction.setFocusedWindow(mInputFocus, focus.getName(), mDisplayId);
- EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus,
+ mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId);
+ EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName,
"reason=UpdateInputWindows");
- ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName);
}
void setFocusedAppLw(ActivityRecord newApp) {
@@ -470,6 +474,8 @@ final class InputMonitor {
boolean mInDrag;
+ private boolean mRecentsAnimationFocusOverride;
+
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -485,10 +491,16 @@ final class InputMonitor {
mInDrag = inDrag;
resetInputConsumers(mInputTransaction);
-
+ mRecentsAnimationFocusOverride = false;
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
- updateInputFocusRequest();
+ if (mRecentsAnimationFocusOverride) {
+ requestFocus(mRecentsAnimationInputConsumer.mWindowHandle.token,
+ mRecentsAnimationInputConsumer.mName);
+ } else {
+ updateInputFocusRequest();
+ }
+
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -526,6 +538,7 @@ final class InputMonitor {
mRecentsAnimationInputConsumer.mWindowHandle)) {
mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
mAddRecentsAnimationInputConsumerHandle = false;
+ mRecentsAnimationFocusOverride = true;
}
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index e18516d7bc3a..62c155a3c198 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -540,7 +540,7 @@ public class LockTaskController {
setStatusBarState(mLockTaskModeState, userId);
setKeyguardState(mLockTaskModeState, userId);
if (oldLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- lockKeyguardIfNeeded();
+ lockKeyguardIfNeeded(userId);
}
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
@@ -886,15 +886,15 @@ public class LockTaskController {
* Helper method for locking the device immediately. This may be necessary when the device
* leaves the pinned mode.
*/
- private void lockKeyguardIfNeeded() {
- if (shouldLockKeyguard()) {
+ private void lockKeyguardIfNeeded(int userId) {
+ if (shouldLockKeyguard(userId)) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
getLockPatternUtils().requireCredentialEntry(USER_ALL);
}
}
- private boolean shouldLockKeyguard() {
+ private boolean shouldLockKeyguard(int userId) {
// This functionality should be kept consistent with
// com.android.settings.security.ScreenPinningSettings (see b/127605586)
try {
@@ -904,7 +904,7 @@ public class LockTaskController {
} catch (Settings.SettingNotFoundException e) {
// Log to SafetyNet for b/127605586
android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, "");
- return getLockPatternUtils().isSecure(USER_CURRENT);
+ return getLockPatternUtils().isSecure(userId);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 0be43ab8d26c..ee5c1f014895 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -165,6 +165,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// hasInitialBounds is set if either activity options or layout has specified bounds. If
// that's set we'll skip some adjustments later to avoid overriding the initial bounds.
boolean hasInitialBounds = false;
+ // hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow is set if the outParams.mBounds
+ // is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
+ // different, we should recalculating the bounds.
+ boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
&& (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
@@ -181,11 +185,12 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
} else if (layout != null && canApplyFreeformPolicy) {
mTmpBounds.set(currentParams.mBounds);
- getLayoutBounds(display, root, layout, mTmpBounds);
+ getLayoutBounds(suggestedDisplayArea, root, layout, mTmpBounds);
if (!mTmpBounds.isEmpty()) {
launchMode = WINDOWING_MODE_FREEFORM;
outParams.mBounds.set(mTmpBounds);
hasInitialBounds = true;
+ hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = true;
if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds);
} else {
if (DEBUG) appendLog("empty-window-layout");
@@ -241,6 +246,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// such as orientation. Otherwise, the app is forcefully launched in maximized. The rest of
// this step is to define the default policy when there is no initial bounds or a fully
// resolved current params from callers.
+
+ // hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay is set if the outParams.mBounds
+ // is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
+ // different, we should recalcuating the bounds.
+ boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = false;
if (display.inFreeformWindowingMode()) {
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
@@ -248,8 +258,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) {
launchMode = WINDOWING_MODE_FREEFORM;
if (outParams.mBounds.isEmpty()) {
- getTaskBounds(root, display, layout, launchMode, hasInitialBounds,
- outParams.mBounds);
+ getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
+ hasInitialBounds, outParams.mBounds);
+ hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = true;
}
if (DEBUG) appendLog("unresizable-freeform");
} else {
@@ -288,6 +299,20 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
mTmpDisplayArea = displayArea;
return true;
});
+ // We may need to recalculate the bounds if the new TaskDisplayArea is different from
+ // the suggested one we used to calculate the bounds.
+ if (mTmpDisplayArea != null && mTmpDisplayArea != suggestedDisplayArea) {
+ if (hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow) {
+ outParams.mBounds.setEmpty();
+ getLayoutBounds(mTmpDisplayArea, root, layout, outParams.mBounds);
+ hasInitialBounds = !outParams.mBounds.isEmpty();
+ } else if (hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay) {
+ outParams.mBounds.setEmpty();
+ getTaskBounds(root, mTmpDisplayArea, layout, launchMode,
+ hasInitialBounds, outParams.mBounds);
+ }
+ }
+
if (mTmpDisplayArea != null) {
taskDisplayArea = mTmpDisplayArea;
mTmpDisplayArea = null;
@@ -303,7 +328,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (phase == PHASE_DISPLAY_AREA) {
return RESULT_CONTINUE;
}
- // TODO(b/152116619): Update the usages of display to use taskDisplayArea below.
// STEP 4: Determine final launch bounds based on resolved windowing mode and activity
// requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
@@ -313,13 +337,13 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// We skip making adjustments if the params are fully resolved from previous results.
if (fullyResolvedCurrentParam) {
if (resolvedMode == WINDOWING_MODE_FREEFORM) {
- // Make sure bounds are in the display if it's possibly in a different display/area.
+ // Make sure bounds are in the displayArea.
if (currentParams.mPreferredTaskDisplayArea != taskDisplayArea) {
- adjustBoundsToFitInDisplay(display, outParams.mBounds);
+ adjustBoundsToFitInDisplayArea(taskDisplayArea, outParams.mBounds);
}
// Even though we want to keep original bounds, we still don't want it to stomp on
// an existing task.
- adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds);
+ adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
}
} else if (taskDisplayArea.inFreeformWindowingMode()) {
if (source != null && source.inFreeformWindowingMode()
@@ -328,9 +352,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
&& source.getDisplayArea() == taskDisplayArea) {
// Set bounds to be not very far from source activity.
cascadeBounds(source.getConfiguration().windowConfiguration.getBounds(),
- display, outParams.mBounds);
+ taskDisplayArea, outParams.mBounds);
}
- getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
+ getTaskBounds(root, taskDisplayArea, layout, resolvedMode, hasInitialBounds,
+ outParams.mBounds);
}
return RESULT_CONTINUE;
}
@@ -500,7 +525,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
&& launchMode == WINDOWING_MODE_PINNED;
}
- private void getLayoutBounds(@NonNull DisplayContent display, @NonNull ActivityRecord root,
+ private void getLayoutBounds(@NonNull TaskDisplayArea displayArea, @NonNull ActivityRecord root,
@NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds) {
final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
@@ -511,10 +536,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// Use stable frame instead of raw frame to avoid launching freeform windows on top of
// stable insets, which usually are system widgets such as sysbar & navbar.
- final Rect displayStableBounds = mTmpStableBounds;
- display.getStableRect(displayStableBounds);
- final int defaultWidth = displayStableBounds.width();
- final int defaultHeight = displayStableBounds.height();
+ final Rect stableBounds = mTmpStableBounds;
+ displayArea.getStableRect(stableBounds);
+ final int defaultWidth = stableBounds.width();
+ final int defaultHeight = stableBounds.height();
int width;
int height;
@@ -525,7 +550,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
width = inOutBounds.width();
height = inOutBounds.height();
} else {
- getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
+ getTaskBounds(root, displayArea, windowLayout, WINDOWING_MODE_FREEFORM,
/* hasInitialBounds */ false, inOutBounds);
width = inOutBounds.width();
height = inOutBounds.height();
@@ -571,7 +596,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
inOutBounds.set(0, 0, width, height);
- inOutBounds.offset(displayStableBounds.left, displayStableBounds.top);
+ inOutBounds.offset(stableBounds.left, stableBounds.top);
final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
inOutBounds.offset(xOffset, yOffset);
@@ -582,13 +607,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
return false;
}
- final DisplayContent display = displayArea.getDisplayContent();
- if (display == null) {
- return false;
- }
final int displayOrientation = orientationFromBounds(displayArea.getBounds());
- final int activityOrientation = resolveOrientation(activity, display,
+ final int activityOrientation = resolveOrientation(activity, displayArea,
displayArea.getBounds());
if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& displayOrientation != activityOrientation) {
@@ -638,19 +659,19 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return orientation;
}
- private void cascadeBounds(@NonNull Rect srcBounds, @NonNull DisplayContent display,
+ private void cascadeBounds(@NonNull Rect srcBounds, @NonNull TaskDisplayArea displayArea,
@NonNull Rect outBounds) {
outBounds.set(srcBounds);
- float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+ float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
final int defaultOffset = (int) (CASCADING_OFFSET_DP * density + 0.5f);
- display.getBounds(mTmpBounds);
+ displayArea.getBounds(mTmpBounds);
final int dx = Math.min(defaultOffset, Math.max(0, mTmpBounds.right - srcBounds.right));
final int dy = Math.min(defaultOffset, Math.max(0, mTmpBounds.bottom - srcBounds.bottom));
outBounds.offset(dx, dy);
}
- private void getTaskBounds(@NonNull ActivityRecord root, @NonNull DisplayContent display,
+ private void getTaskBounds(@NonNull ActivityRecord root, @NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
@NonNull Rect inOutBounds) {
if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
@@ -669,7 +690,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return;
}
- final int orientation = resolveOrientation(root, display, inOutBounds);
+ final int orientation = resolveOrientation(root, displayArea, inOutBounds);
if (orientation != SCREEN_ORIENTATION_PORTRAIT
&& orientation != SCREEN_ORIENTATION_LANDSCAPE) {
throw new IllegalStateException(
@@ -678,7 +699,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
// First we get the default size we want.
- getDefaultFreeformSize(display, layout, orientation, mTmpBounds);
+ getDefaultFreeformSize(displayArea, layout, orientation, mTmpBounds);
if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
// We're here because either input parameters specified initial bounds, or the suggested
// bounds have the same size of the default freeform size. We should use the suggested
@@ -688,22 +709,24 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds);
} else {
// Meh, orientation doesn't match. Let's rotate inOutBounds in-place.
- centerBounds(display, inOutBounds.height(), inOutBounds.width(), inOutBounds);
+ centerBounds(displayArea, inOutBounds.height(), inOutBounds.width(),
+ inOutBounds);
if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds);
}
} else {
// We are here either because there is no suggested bounds, or the suggested bounds is
// a cascade from source activity. We should use the default freeform size and center it
- // to the center of suggested bounds (or the display if no suggested bounds). The
- // default size might be too big to center to source activity bounds in display, so we
- // may need to move it back to the display.
- centerBounds(display, mTmpBounds.width(), mTmpBounds.height(), inOutBounds);
- adjustBoundsToFitInDisplay(display, inOutBounds);
+ // to the center of suggested bounds (or the displayArea if no suggested bounds). The
+ // default size might be too big to center to source activity bounds in displayArea, so
+ // we may need to move it back to the displayArea.
+ centerBounds(displayArea, mTmpBounds.width(), mTmpBounds.height(),
+ inOutBounds);
+ adjustBoundsToFitInDisplayArea(displayArea, inOutBounds);
if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
}
// Lastly we adjust bounds to avoid conflicts with other tasks as much as possible.
- adjustBoundsToAvoidConflictInDisplay(display, inOutBounds);
+ adjustBoundsToAvoidConflictInDisplayArea(displayArea, inOutBounds);
}
private int convertOrientationToScreenOrientation(int orientation) {
@@ -717,13 +740,14 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
}
- private int resolveOrientation(@NonNull ActivityRecord root, @NonNull DisplayContent display,
- @NonNull Rect bounds) {
+ private int resolveOrientation(@NonNull ActivityRecord root,
+ @NonNull TaskDisplayArea displayArea, @NonNull Rect bounds) {
int orientation = resolveOrientation(root);
if (orientation == SCREEN_ORIENTATION_LOCKED) {
orientation = bounds.isEmpty()
- ? convertOrientationToScreenOrientation(display.getConfiguration().orientation)
+ ? convertOrientationToScreenOrientation(
+ displayArea.getConfiguration().orientation)
: orientationFromBounds(bounds);
if (DEBUG) {
appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
@@ -743,19 +767,17 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return orientation;
}
- private void getDefaultFreeformSize(@NonNull DisplayContent display,
+ private void getDefaultFreeformSize(@NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
- // Default size, which is letterboxing/pillarboxing in display. That's to say the large
- // dimension of default size is the small dimension of display size, and the small dimension
- // of default size is calculated to keep the same aspect ratio as the display's. Here we use
- // stable bounds of displays because that indicates the area that isn't occupied by system
- // widgets (e.g. sysbar and navbar).
- final Rect displayStableBounds = mTmpStableBounds;
- display.getStableRect(displayStableBounds);
- final int portraitHeight =
- Math.min(displayStableBounds.width(), displayStableBounds.height());
- final int otherDimension =
- Math.max(displayStableBounds.width(), displayStableBounds.height());
+ // Default size, which is letterboxing/pillarboxing in displayArea. That's to say the large
+ // dimension of default size is the small dimension of displayArea size, and the small
+ // dimension of default size is calculated to keep the same aspect ratio as the
+ // displayArea's. Here we use stable bounds of displayArea because that indicates the area
+ // that isn't occupied by system widgets (e.g. sysbar and navbar).
+ final Rect stableBounds = mTmpStableBounds;
+ displayArea.getStableRect(stableBounds);
+ final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height());
+ final int otherDimension = Math.max(stableBounds.width(), stableBounds.height());
final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
: portraitWidth;
@@ -764,7 +786,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// Get window size based on Nexus 5x screen, we assume that this is enough to show content
// of activities.
- final float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+ final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f);
final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f);
final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight
@@ -781,83 +803,83 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
bounds.set(0, 0, width, height);
- bounds.offset(displayStableBounds.left, displayStableBounds.top);
+ bounds.offset(stableBounds.left, stableBounds.top);
}
/**
* Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
- * centers at its center or display's app bounds center if inOutBounds is empty.
+ * centers at its center or displayArea's app bounds center if inOutBounds is empty.
*/
- private void centerBounds(@NonNull DisplayContent display, int width, int height,
+ private void centerBounds(@NonNull TaskDisplayArea displayArea, int width, int height,
@NonNull Rect inOutBounds) {
if (inOutBounds.isEmpty()) {
- display.getStableRect(inOutBounds);
+ displayArea.getStableRect(inOutBounds);
}
final int left = inOutBounds.centerX() - width / 2;
final int top = inOutBounds.centerY() - height / 2;
inOutBounds.set(left, top, left + width, top + height);
}
- private void adjustBoundsToFitInDisplay(@NonNull DisplayContent display,
+ private void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
@NonNull Rect inOutBounds) {
- final Rect displayStableBounds = mTmpStableBounds;
- display.getStableRect(displayStableBounds);
+ final Rect stableBounds = mTmpStableBounds;
+ displayArea.getStableRect(stableBounds);
- if (displayStableBounds.width() < inOutBounds.width()
- || displayStableBounds.height() < inOutBounds.height()) {
- // There is no way for us to fit the bounds in the display without changing width
- // or height. Just move the start to align with the display.
+ if (stableBounds.width() < inOutBounds.width()
+ || stableBounds.height() < inOutBounds.height()) {
+ // There is no way for us to fit the bounds in the displayArea without changing width
+ // or height. Just move the start to align with the displayArea.
final int layoutDirection =
mSupervisor.mRootWindowContainer.getConfiguration().getLayoutDirection();
final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
- ? displayStableBounds.right - inOutBounds.right + inOutBounds.left
- : displayStableBounds.left;
- inOutBounds.offsetTo(left, displayStableBounds.top);
+ ? stableBounds.right - inOutBounds.right + inOutBounds.left
+ : stableBounds.left;
+ inOutBounds.offsetTo(left, stableBounds.top);
return;
}
final int dx;
- if (inOutBounds.right > displayStableBounds.right) {
- // Right edge is out of display.
- dx = displayStableBounds.right - inOutBounds.right;
- } else if (inOutBounds.left < displayStableBounds.left) {
- // Left edge is out of display.
- dx = displayStableBounds.left - inOutBounds.left;
+ if (inOutBounds.right > stableBounds.right) {
+ // Right edge is out of displayArea.
+ dx = stableBounds.right - inOutBounds.right;
+ } else if (inOutBounds.left < stableBounds.left) {
+ // Left edge is out of displayArea.
+ dx = stableBounds.left - inOutBounds.left;
} else {
- // Vertical edges are all in display.
+ // Vertical edges are all in displayArea.
dx = 0;
}
final int dy;
- if (inOutBounds.top < displayStableBounds.top) {
- // Top edge is out of display.
- dy = displayStableBounds.top - inOutBounds.top;
- } else if (inOutBounds.bottom > displayStableBounds.bottom) {
- // Bottom edge is out of display.
- dy = displayStableBounds.bottom - inOutBounds.bottom;
+ if (inOutBounds.top < stableBounds.top) {
+ // Top edge is out of displayArea.
+ dy = stableBounds.top - inOutBounds.top;
+ } else if (inOutBounds.bottom > stableBounds.bottom) {
+ // Bottom edge is out of displayArea.
+ dy = stableBounds.bottom - inOutBounds.bottom;
} else {
- // Horizontal edges are all in display.
+ // Horizontal edges are all in displayArea.
dy = 0;
}
inOutBounds.offset(dx, dy);
}
/**
- * Adjusts input bounds to avoid conflict with existing tasks in the display.
+ * Adjusts input bounds to avoid conflict with existing tasks in the displayArea.
*
* If the input bounds conflict with existing tasks, this method scans the bounds in a series of
- * directions to find a location where the we can put the bounds in display without conflict
+ * directions to find a location where the we can put the bounds in displayArea without conflict
* with any other tasks.
*
- * It doesn't try to adjust bounds that's not fully in the given display.
+ * It doesn't try to adjust bounds that's not fully in the given displayArea.
*
- * @param display the display which tasks are to check
+ * @param displayArea the displayArea which tasks are to check
* @param inOutBounds the bounds used to input initial bounds and output result bounds
*/
- private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display,
+ private void adjustBoundsToAvoidConflictInDisplayArea(@NonNull TaskDisplayArea displayArea,
@NonNull Rect inOutBounds) {
final List<Rect> taskBoundsToCheck = new ArrayList<>();
- display.forAllRootTasks(task -> {
+ displayArea.forAllRootTasks(task -> {
if (!task.inFreeformWindowingMode()) {
return;
}
@@ -866,28 +888,28 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
taskBoundsToCheck.add(task.getChildAt(j).getBounds());
}
}, false /* traverseTopToBottom */);
- adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
+ adjustBoundsToAvoidConflict(displayArea.getBounds(), taskBoundsToCheck, inOutBounds);
}
/**
- * Adjusts input bounds to avoid conflict with provided display bounds and list of tasks bounds
- * for the display.
+ * Adjusts input bounds to avoid conflict with provided displayArea bounds and list of tasks
+ * bounds for the displayArea.
*
* Scans the bounds in directions to find a candidate location that does not conflict with the
- * provided list of task bounds. If starting bounds are outside the display bounds or if no
+ * provided list of task bounds. If starting bounds are outside the displayArea bounds or if no
* suitable candidate bounds are found, the method returns the input bounds.
*
- * @param displayBounds display bounds used to restrict the candidate bounds
+ * @param displayAreaBounds displayArea bounds used to restrict the candidate bounds
* @param taskBoundsToCheck list of task bounds to check for conflict
* @param inOutBounds the bounds used to input initial bounds and output result bounds
*/
@VisibleForTesting
- void adjustBoundsToAvoidConflict(@NonNull Rect displayBounds,
+ void adjustBoundsToAvoidConflict(@NonNull Rect displayAreaBounds,
@NonNull List<Rect> taskBoundsToCheck,
@NonNull Rect inOutBounds) {
- if (!displayBounds.contains(inOutBounds)) {
- // The initial bounds are already out of display. The scanning algorithm below doesn't
- // work so well with them.
+ if (!displayAreaBounds.contains(inOutBounds)) {
+ // The initial bounds are already out of displayArea. The scanning algorithm below
+ // doesn't work so well with them.
return;
}
@@ -897,7 +919,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return;
}
- calculateCandidateShiftDirections(displayBounds, inOutBounds);
+ calculateCandidateShiftDirections(displayAreaBounds, inOutBounds);
for (int direction : mTmpDirections) {
if (direction == Gravity.NO_GRAVITY) {
// We exhausted candidate directions, give up.
@@ -906,12 +928,12 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
mTmpBounds.set(inOutBounds);
while (boundsConflict(taskBoundsToCheck, mTmpBounds)
- && displayBounds.contains(mTmpBounds)) {
- shiftBounds(direction, displayBounds, mTmpBounds);
+ && displayAreaBounds.contains(mTmpBounds)) {
+ shiftBounds(direction, displayAreaBounds, mTmpBounds);
}
if (!boundsConflict(taskBoundsToCheck, mTmpBounds)
- && displayBounds.contains(mTmpBounds)) {
+ && displayAreaBounds.contains(mTmpBounds)) {
// Found a candidate. Just use this.
inOutBounds.set(mTmpBounds);
if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 98eb11f8a970..ee4c66d05eb4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -135,6 +135,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mSyncId = mSyncEngine.startSyncSet(this);
}
+ @VisibleForTesting
+ int getSyncId() {
+ return mSyncId;
+ }
+
/**
* Formally starts the transition. Participants can be collected before this is started,
* but this won't consider itself ready until started -- even if all the participants have
@@ -271,12 +276,17 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Commit all going-invisible containers
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar == null || ar.mVisibleRequested) {
- continue;
+ if (ar != null && !ar.isVisibleRequested()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Commit activity becoming invisible: %s", ar);
+ ar.commitVisibility(false /* visible */, false /* performLayout */);
+ }
+ final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
+ if (wt != null && !wt.isVisibleRequested()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Commit wallpaper becoming invisible: %s", ar);
+ wt.commitVisibility(false /* visible */);
}
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- " Commit activity becoming invisible: %s", ar);
- ar.commitVisibility(false /* visible */, false /* performLayout */);
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1a3138d492c8..7c5afa8282ee 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -126,12 +126,18 @@ class WallpaperController {
}
mFindResults.resetTopWallpaper = true;
- if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
- && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
-
- // If this window's app token is hidden and not animating, it is of no interest to us.
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
- return false;
+ if (mService.mAtmService.getTransitionController().getTransitionPlayer() == null) {
+ if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
+ && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
+ // If this window's app token is hidden and not animating, it is of no interest.
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
+ return false;
+ }
+ } else {
+ if (w.mActivityRecord != null && !w.mActivityRecord.isVisibleRequested()) {
+ // An activity that is not going to remain visible shouldn't be the target.
+ return false;
+ }
}
if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
+ " mDrawState=" + w.mWinAnimator.mDrawState);
@@ -227,7 +233,10 @@ class WallpaperController {
}
boolean isWallpaperVisible() {
- return isWallpaperVisible(mWallpaperTarget);
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; --i) {
+ if (mWallpaperTokens.get(i).isVisible()) return true;
+ }
+ return false;
}
/**
@@ -240,7 +249,7 @@ class WallpaperController {
}
}
- private boolean isWallpaperVisible(WindowState wallpaperTarget) {
+ private boolean shouldWallpaperBeVisible(WindowState wallpaperTarget) {
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + " prev="
+ mPrevWallpaperTarget);
@@ -255,18 +264,18 @@ class WallpaperController {
}
void updateWallpaperVisibility() {
- final boolean visible = isWallpaperVisible(mWallpaperTarget);
+ final boolean visible = shouldWallpaperBeVisible(mWallpaperTarget);
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.updateWallpaperVisibility(visible);
+ token.setVisibility(visible);
}
}
- void hideDeferredWallpapersIfNeeded() {
- if (mDeferredHideWallpaper != null) {
- hideWallpapers(mDeferredHideWallpaper);
- mDeferredHideWallpaper = null;
+ void hideDeferredWallpapersIfNeededLegacy() {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
+ token.commitVisibility(false);
}
}
@@ -275,18 +284,9 @@ class WallpaperController {
&& (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
return;
}
- if (mWallpaperTarget != null
- && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) {
- // Defer hiding the wallpaper when app transition is running until the animations
- // are done.
- mDeferredHideWallpaper = winGoingAway;
- return;
- }
-
- final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
- token.hideWallpaperToken(wasDeferred, "hideWallpapers");
+ token.setVisibility(false);
if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
Slog.d(TAG, "Hiding wallpaper " + token
+ " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
@@ -616,7 +616,7 @@ class WallpaperController {
// The window is visible to the compositor...but is it visible to the user?
// That is what the wallpaper cares about.
- final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
+ final boolean visible = mWallpaperTarget != null;
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+ mDisplayContent.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 43303d4a5d7e..717775605c94 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -19,7 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -34,6 +34,8 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
+import com.android.internal.protolog.common.ProtoLog;
+
import java.util.function.Consumer;
/**
@@ -43,6 +45,8 @@ class WallpaperWindowToken extends WindowToken {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
+ private boolean mVisibleRequested = false;
+
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
@@ -57,18 +61,16 @@ class WallpaperWindowToken extends WindowToken {
}
@Override
+ WallpaperWindowToken asWallpaperToken() {
+ return this;
+ }
+
+ @Override
void setExiting() {
super.setExiting();
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}
- void hideWallpaperToken(boolean wasDeferred, String reason) {
- for (int j = mChildren.size() - 1; j >= 0; j--) {
- final WindowState wallpaper = mChildren.get(j);
- wallpaper.hideWallpaperWindow(wasDeferred, reason);
- }
- }
-
void sendWindowWallpaperCommand(
String action, int x, int y, int z, Bundle extras, boolean sync) {
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
@@ -93,24 +95,6 @@ class WallpaperWindowToken extends WindowToken {
}
}
- void updateWallpaperVisibility(boolean visible) {
- if (isVisible() != visible) {
- mWmService.mAtmService.getTransitionController().collect(this);
- // Need to do a layout to ensure the wallpaper now has the correct size.
- mDisplayContent.setLayoutNeeded();
- }
-
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
- if (visible) {
- wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
- }
-
- wallpaper.dispatchWallpaperVisibility(visible);
- }
- }
-
/**
* Starts {@param anim} on all children.
*/
@@ -122,16 +106,16 @@ class WallpaperWindowToken extends WindowToken {
}
void updateWallpaperWindows(boolean visible) {
-
if (isVisible() != visible) {
if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
"Wallpaper token " + token + " visible=" + visible);
- mWmService.mAtmService.getTransitionController().collect(this);
- // Need to do a layout to ensure the wallpaper now has the correct size.
- mDisplayContent.setLayoutNeeded();
+ setVisibility(visible);
}
-
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ if (mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ return;
+ }
+
final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
if (visible && wallpaperTarget != null) {
@@ -153,19 +137,52 @@ class WallpaperWindowToken extends WindowToken {
}
}
- for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
- final WindowState wallpaper = mChildren.get(wallpaperNdx);
+ setVisible(visible);
+ }
- if (visible) {
- wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
+ private void setVisible(boolean visible) {
+ final boolean wasClientVisible = isClientVisible();
+ setClientVisible(visible);
+ if (visible && !wasClientVisible) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState wallpaper = mChildren.get(i);
+ wallpaper.requestUpdateWallpaperIfNeeded();
}
+ }
+ }
- // First, make sure the client has the current visibility state.
- wallpaper.dispatchWallpaperVisibility(visible);
+ /**
+ * Sets the requested visibility of this token. The visibility may not be if this is part of a
+ * transition. In that situation, make sure to call {@link #commitVisibility} when done.
+ */
+ void setVisibility(boolean visible) {
+ // Before setting mVisibleRequested so we can track changes.
+ mWmService.mAtmService.getTransitionController().collect(this);
+
+ setVisibleRequested(visible);
- if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
- + wallpaper);
+ // If in a transition, defer commits for activities that are going invisible
+ if (!visible && (mWmService.mAtmService.getTransitionController().inTransition()
+ || getDisplayContent().mAppTransition.isRunning())) {
+ return;
}
+
+ commitVisibility(visible);
+ }
+
+ /**
+ * Commits the visibility of this token. This will directly update the visibility without
+ * regard for other state (like being in a transition).
+ */
+ void commitVisibility(boolean visible) {
+ if (visible == isVisible()) return;
+
+ ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+ "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
+ isVisible(), mVisibleRequested);
+
+ setVisibleRequested(visible);
+ setVisible(visible);
}
@Override
@@ -186,9 +203,10 @@ class WallpaperWindowToken extends WindowToken {
}
boolean hasVisibleNotDrawnWallpaper() {
+ if (!isVisible()) return false;
for (int j = mChildren.size() - 1; j >= 0; --j) {
final WindowState wallpaper = mChildren.get(j);
- if (wallpaper.hasVisibleNotDrawnWallpaper()) {
+ if (!wallpaper.isDrawn() && wallpaper.isVisible()) {
return true;
}
}
@@ -210,6 +228,21 @@ class WallpaperWindowToken extends WindowToken {
return false;
}
+ void setVisibleRequested(boolean visible) {
+ if (mVisibleRequested == visible) return;
+ mVisibleRequested = visible;
+ setInsetsFrozen(!visible);
+ }
+
+ @Override
+ boolean isVisibleRequested() {
+ return mVisibleRequested;
+ }
+
+ @Override
+ boolean isVisible() {
+ return isClientVisible();
+ }
@Override
public String toString() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index dd4ee877c05b..0c4ff2fe6365 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2684,14 +2684,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
- if (AppTransition.isClosingTransitOld(transit)) {
- // Freezes the insets state when the window is in app exiting transition, to
- // ensure the exiting window won't receive unexpected insets changes from the
- // next window.
- task.forAllWindows(w -> {
- w.freezeInsetsState();
- }, true /* traverseTopToBottom */);
- }
mDisplayContent.showImeScreenshot();
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
@@ -3068,6 +3060,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/** Cheap way of doing cast and instanceof. */
+ WallpaperWindowToken asWallpaperToken() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
DisplayArea asDisplayArea() {
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index e03198d63b4c..7450782364f4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -66,7 +66,7 @@ public abstract class WindowManagerInternal {
/**
* Is trace enabled or not.
*/
- boolean isEnabled();
+ boolean isAccessibilityTracingEnabled();
/**
* Add an accessibility trace entry.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 70b0f5888766..c9e1605f7f0d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3971,6 +3971,21 @@ public class WindowManagerService extends IWindowManager.Stub
? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
}
+ /** Returns a string representing the given {@link LetterboxBackgroundType}. */
+ static String letterboxBackgroundTypeToString(
+ @LetterboxBackgroundType int backgroundType) {
+ switch (backgroundType) {
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return "LETTERBOX_BACKGROUND_SOLID_COLOR";
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
+ default:
+ return "unknown=" + backgroundType;
+ }
+ }
+
@Override
public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
if (!checkCallingPermission(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a35f4dea643a..deb3913a32ed 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -141,11 +141,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
@@ -358,7 +356,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private boolean mForceHideNonSystemOverlayWindow;
boolean mAppFreezing;
boolean mHidden = true; // Used to determine if to show child windows.
- boolean mWallpaperVisible; // for wallpaper, what was last vis report?
private boolean mDragResizing;
private boolean mDragResizingChangeReported = true;
private int mResizeMode;
@@ -797,7 +794,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* {@link InsetsStateController#notifyInsetsChanged}.
*/
boolean isReadyToDispatchInsetsState() {
- return isVisible() && mFrozenInsetsState == null;
+ return isVisibleRequested() && mFrozenInsetsState == null;
}
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@@ -1715,7 +1712,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
boolean isVisibleRequested() {
- return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
+ if (mToken != null && (mActivityRecord != null || mToken.asWallpaperToken() != null)) {
+ // Currently only ActivityRecord and WallpaperToken support visibleRequested.
+ return isVisible() && mToken.isVisibleRequested();
+ }
+ return isVisible();
}
/**
@@ -1745,8 +1746,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* {@code false} otherwise.
*/
boolean wouldBeVisibleIfPolicyIgnored() {
- return mHasSurface && !isParentWindowHidden()
- && !mAnimatingExit && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
+ if (!mHasSurface || isParentWindowHidden() || mAnimatingExit || mDestroying) {
+ return false;
+ }
+ final boolean isWallpaper = mToken != null && mToken.asWallpaperToken() != null;
+ return !isWallpaper || mToken.isVisible();
}
/**
@@ -1804,6 +1808,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return ((!isParentWindowHidden() && atoken.isVisible())
|| isAnimating(TRANSITION | PARENTS));
}
+ final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
+ if (wtoken != null) {
+ return !isParentWindowHidden() && wtoken.isVisible();
+ }
return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
}
@@ -1943,8 +1951,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// When there is keyguard, wallpaper could be placed over the secure app
// window but invisible. We need to check wallpaper visibility explicitly
// to determine if it's occluding apps.
- return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
- || (mIsWallpaper && mWallpaperVisible))
+ final boolean isWallpaper = mToken != null && mToken.asWallpaperToken() != null;
+ return ((!isWallpaper && mAttrs.format == PixelFormat.OPAQUE)
+ || (isWallpaper && mToken.isVisible()))
&& isDrawn() && !isAnimating(TRANSITION | PARENTS);
}
@@ -3224,7 +3233,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
- final boolean clientVisible = mActivityRecord.isClientVisible();
+ if (mToken == null) return;
+
+ final boolean clientVisible = mToken.isClientVisible();
+ // TODO(shell-transitions): This is currently only applicable to app windows, BUT we
+ // want to extend the "starting" concept to other windows.
if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
// Don't hide the starting window.
return;
@@ -3608,9 +3621,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
return;
}
- // If the activity is invisible or going invisible, don't report either since it is going
- // away. This is likely during a transition so we want to preserve the original state.
- if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+ // If this is an activity or wallpaper and is invisible or going invisible, don't report
+ // either since it is going away. This is likely during a transition so we want to preserve
+ // the original state.
+ if ((mActivityRecord != null || mToken.asWallpaperToken() != null)
+ && !mToken.isVisibleRequested()) {
return;
}
@@ -4024,8 +4039,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
pw.println(prefix + "mIsImWindow=" + mIsImWindow
+ " mIsWallpaper=" + mIsWallpaper
- + " mIsFloatingLayer=" + mIsFloatingLayer
- + " mWallpaperVisible=" + mWallpaperVisible);
+ + " mIsFloatingLayer=" + mIsFloatingLayer);
}
if (dumpAll) {
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
@@ -4839,61 +4853,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
- void hideWallpaperWindow(boolean wasDeferred, String reason) {
- for (int j = mChildren.size() - 1; j >= 0; --j) {
- final WindowState c = mChildren.get(j);
- c.hideWallpaperWindow(wasDeferred, reason);
- }
- if (!mWinAnimator.mLastHidden || wasDeferred) {
- mWinAnimator.hide(getGlobalTransaction(), reason);
- getDisplayContent().mWallpaperController.mDeferredHideWallpaper = null;
- dispatchWallpaperVisibility(false);
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent != null) {
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) {
- mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
- displayContent.pendingLayoutChanges);
- }
- }
- }
- }
-
- /**
- * Check wallpaper window for visibility change and notify window if so.
- * @param visible Current visibility.
- */
- void dispatchWallpaperVisibility(final boolean visible) {
- final boolean hideAllowed =
- getDisplayContent().mWallpaperController.mDeferredHideWallpaper == null;
-
- // Only send notification if the visibility actually changed and we are not trying to hide
- // the wallpaper when we are deferring hiding of the wallpaper.
- if (mWallpaperVisible != visible && (hideAllowed || visible)) {
- mWallpaperVisible = visible;
- try {
- if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "Updating vis of wallpaper " + this
- + ": " + visible + " from:\n" + Debug.getCallers(4, " "));
- mClient.dispatchAppVisibility(visible);
- } catch (RemoteException e) {
- }
- }
- }
-
- boolean hasVisibleNotDrawnWallpaper() {
- if (mWallpaperVisible && !isDrawn()) {
- return true;
- }
- for (int j = mChildren.size() - 1; j >= 0; --j) {
- final WindowState c = mChildren.get(j);
- if (c.hasVisibleNotDrawnWallpaper()) {
- return true;
- }
- }
- return false;
- }
-
void updateReportedVisibility(UpdateReportedVisibilityResults results) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ece256e8c591..ebbebbb702d8 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -57,7 +57,6 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.Region;
import android.os.Debug;
import android.os.Trace;
import android.util.Slog;
@@ -575,10 +574,7 @@ class WindowStateAnimator {
setSurfaceBoundariesLocked(t);
- if (mIsWallpaper && !w.mWallpaperVisible) {
- // Wallpaper is no longer visible and there is no wp target => hide it.
- hide(t, "prepareSurfaceLocked");
- } else if (w.isParentWindowHidden() || !w.isOnScreen()) {
+ if (w.isParentWindowHidden() || !w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
mWallpaperControllerLocked.hideWallpapers(w);
@@ -631,9 +627,6 @@ class WindowStateAnimator {
if (showSurfaceRobustlyLocked(t)) {
mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
- if (mIsWallpaper) {
- w.dispatchWallpaperVisibility(true);
- }
final DisplayContent displayContent = w.getDisplayContent();
if (!displayContent.getLastHasContent()) {
// This draw means the difference between unique content and mirroring.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e87ee918e0f0..8867aa747379 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -112,6 +113,9 @@ class WindowToken extends WindowContainer<WindowState> {
*/
private final boolean mFromClientToken;
+ /** Have we told the window clients to show themselves? */
+ private boolean mClientVisible;
+
/**
* Used to fix the transform of the token to be rotated to a rotation different than it's
* display. The window frames and surfaces corresponding to this token will be layouted and
@@ -397,6 +401,21 @@ class WindowToken extends WindowContainer<WindowState> {
return builder;
}
+ boolean isClientVisible() {
+ return mClientVisible;
+ }
+
+ void setClientVisible(boolean clientVisible) {
+ if (mClientVisible == clientVisible) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
+ Debug.getCallers(5));
+ mClientVisible = clientVisible;
+ sendAppVisibilityToClients();
+ }
+
boolean hasFixedRotationTransform() {
return mFixedRotationTransformState != null;
}
@@ -736,4 +755,13 @@ class WindowToken extends WindowContainer<WindowState> {
boolean isFromClient() {
return mFromClientToken;
}
+
+ /** @see WindowState#freezeInsetsState() */
+ void setInsetsFrozen(boolean freeze) {
+ if (freeze) {
+ forAllWindows(WindowState::freezeInsetsState, true /* traverseTopToBottom */);
+ } else {
+ forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */);
+ }
+ }
}
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index c285ef519e44..6a8f6d419786 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -30,7 +30,6 @@ xsd_config {
gen_writer: true,
}
-
xsd_config {
name: "display-device-config",
srcs: ["display-device-config/display-device-config.xsd"],
@@ -38,6 +37,12 @@ xsd_config {
package_name: "com.android.server.display.config",
}
+xsd_config {
+ name: "display-layout-config",
+ srcs: ["display-layout-config/display-layout-config.xsd"],
+ api_dir: "display-layout-config/schema",
+ package_name: "com.android.server.display.config.layout",
+}
xsd_config {
name: "cec-config",
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7d705c1fac69..e4b961299f12 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -1,23 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+ Copyright (C) 2020 The Android Open Source Project
-<!-- This defines the format of the XML file generated by
- ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
- ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This defines the format of the XML file used to provide static configuration values
+ for the displays on a device.
+ It is parsed in com/android/server/display/DisplayDeviceConfig.java
-->
<xs:schema version="2.0"
elementFormDefault="qualified"
diff --git a/services/core/xsd/display-layout-config/OWNERS b/services/core/xsd/display-layout-config/OWNERS
new file mode 100644
index 000000000000..20b75be9f11f
--- /dev/null
+++ b/services/core/xsd/display-layout-config/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/display/OWNERS
+
+flc@google.com
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
new file mode 100644
index 000000000000..c542c0d0c382
--- /dev/null
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This defines the format of the XML file used to defines how displays are laid out
+ for a given device-state.
+ It is parsed in com/android/server/display/layout/DeviceStateToLayoutMap.java
+ More information on device-state can be found in DeviceStateManager.java
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="layouts">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element type="layout" name="layout" minOccurs="1" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Ensures only one layout is allowed per device state. -->
+ <xs:unique name="UniqueState">
+ <xs:selector xpath="layout" />
+ <xs:field xpath="@state" />
+ </xs:unique>
+ </xs:element>
+
+ <!-- Type definitions -->
+
+ <xs:complexType name="layout">
+ <xs:sequence>
+ <xs:element name="state" type="xs:nonNegativeInteger" />
+ <xs:element name="display" type="display" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="display">
+ <xs:sequence>
+ <xs:element name="address" type="xs:nonNegativeInteger"/>
+ </xs:sequence>
+ <xs:attribute name="enabled" type="xs:boolean" use="optional" />
+ <xs:attribute name="isDefault" type="xs:boolean" use="optional" />
+ </xs:complexType>
+</xs:schema>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
new file mode 100644
index 000000000000..817188509f81
--- /dev/null
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -0,0 +1,34 @@
+// Signature format: 2.0
+package com.android.server.display.config.layout {
+
+ public class Display {
+ ctor public Display();
+ method public java.math.BigInteger getAddress();
+ method public boolean getEnabled();
+ method public boolean getIsDefault();
+ method public void setAddress(java.math.BigInteger);
+ method public void setEnabled(boolean);
+ method public void setIsDefault(boolean);
+ }
+
+ public class Layout {
+ ctor public Layout();
+ method public java.util.List<com.android.server.display.config.layout.Display> getDisplay();
+ method public java.math.BigInteger getState();
+ method public void setState(java.math.BigInteger);
+ }
+
+ public class Layouts {
+ ctor public Layouts();
+ method public java.util.List<com.android.server.display.config.layout.Layout> getLayout();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.display.config.layout.Layouts read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/services/core/xsd/display-layout-config/schema/last_current.txt b/services/core/xsd/display-layout-config/schema/last_current.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/display-layout-config/schema/last_current.txt
diff --git a/services/core/xsd/display-layout-config/schema/last_removed.txt b/services/core/xsd/display-layout-config/schema/last_removed.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/display-layout-config/schema/last_removed.txt
diff --git a/services/core/xsd/display-layout-config/schema/removed.txt b/services/core/xsd/display-layout-config/schema/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/services/core/xsd/display-layout-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 28e9acf8d883..04af5c93160d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1583,8 +1583,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
public String[] getPersonalAppsForSuspension(int userId) {
- return new PersonalAppsSuspensionHelper(
- mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
+ return PersonalAppsSuspensionHelper.forUser(mContext, userId)
.getPersonalAppsForSuspension();
}
@@ -1599,6 +1598,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
mSafetyChecker = safetyChecker;
}
+
+ void dumpPerUserData(IndentingPrintWriter pw, @UserIdInt int userId) {
+ PersonalAppsSuspensionHelper.forUser(mContext, userId).dump(pw);
+ }
}
/**
@@ -9161,11 +9164,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void dumpDevicePolicyData(IndentingPrintWriter pw) {
+ private void dumpPerUserData(IndentingPrintWriter pw) {
int userCount = mUserData.size();
- for (int u = 0; u < userCount; u++) {
- DevicePolicyData policy = getUserData(mUserData.keyAt(u));
+ for (int userId = 0; userId < userCount; userId++) {
+ DevicePolicyData policy = getUserData(mUserData.keyAt(userId));
policy.dump(pw);
+ pw.println();
+
+ pw.increaseIndent();
+ mInjector.dumpPerUserData(pw, userId);
+ pw.decreaseIndent();
+ pw.println();
}
}
@@ -9183,7 +9192,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.println();
mDeviceAdminServiceController.dump(pw);
pw.println();
- dumpDevicePolicyData(pw);
+ dumpPerUserData(pw);
pw.println();
mConstants.dump(pw);
pw.println();
@@ -9229,20 +9238,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.increaseIndent();
dumpResources(pw, mContext, "cross_profile_apps", R.array.cross_profile_apps);
dumpResources(pw, mContext, "vendor_cross_profile_apps", R.array.vendor_cross_profile_apps);
+ dumpResources(pw, mContext, "config_packagesExemptFromSuspension",
+ R.array.config_packagesExemptFromSuspension);
pw.decreaseIndent();
pw.println();
}
static void dumpResources(IndentingPrintWriter pw, Context context, String resName, int resId) {
- String[] apps = context.getResources().getStringArray(resId);
- if (apps == null || apps.length == 0) {
- pw.printf("%s: empty\n", resName);
+ dumpApps(pw, resName, context.getResources().getStringArray(resId));
+ }
+
+ static void dumpApps(IndentingPrintWriter pw, String name, String[] apps) {
+ dumpApps(pw, name, Arrays.asList(apps));
+ }
+
+ static void dumpApps(IndentingPrintWriter pw, String name, List apps) {
+ if (apps == null || apps.isEmpty()) {
+ pw.printf("%s: empty\n", name);
return;
}
- pw.printf("%s: %d app%s\n", resName, apps.length, apps.length == 1 ? "" : "s");
+ int size = apps.size();
+ pw.printf("%s: %d app%s\n", name, size, size == 1 ? "" : "s");
pw.increaseIndent();
- for (int i = 0; i < apps.length; i++) {
- pw.printf("%d: %s\n", i, apps[i]);
+ for (int i = 0; i < size; i++) {
+ pw.printf("%d: %s\n", i, apps.get(i));
}
pw.decreaseIndent();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index c687184265c1..37dbfc170aff 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -20,6 +20,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -29,10 +30,12 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -49,7 +52,7 @@ import java.util.Set;
/**
* Utility class to find what personal apps should be suspended to limit personal device use.
*/
-public class PersonalAppsSuspensionHelper {
+public final class PersonalAppsSuspensionHelper {
private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
// Flags to get all packages even if the user is still locked.
@@ -60,9 +63,17 @@ public class PersonalAppsSuspensionHelper {
private final PackageManager mPackageManager;
/**
+ * Factory method
+ */
+ public static PersonalAppsSuspensionHelper forUser(Context context, @UserIdInt int userId) {
+ return new PersonalAppsSuspensionHelper(context.createContextAsUser(UserHandle.of(userId),
+ /* flags= */ 0));
+ }
+
+ /**
* @param context Context for the user whose apps should to be suspended.
*/
- public PersonalAppsSuspensionHelper(Context context) {
+ private PersonalAppsSuspensionHelper(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
}
@@ -181,4 +192,21 @@ public class PersonalAppsSuspensionHelper {
iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder);
return new AccessibilityManager(mContext, service, userId);
}
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("PersonalAppsSuspensionHelper");
+ pw.increaseIndent();
+
+ DevicePolicyManagerService.dumpApps(pw, "critical packages", getCriticalPackages());
+ DevicePolicyManagerService.dumpApps(pw, "launcher packages", getSystemLauncherPackages());
+ DevicePolicyManagerService.dumpApps(pw, "accessibility services",
+ getAccessibilityServices());
+ DevicePolicyManagerService.dumpApps(pw, "input method packages", getInputMethodPackages());
+ pw.printf("SMS package: %s\n", Telephony.Sms.getDefaultSmsPackage(mContext));
+ pw.printf("Settings package: %s\n", getSettingsPackageName());
+ DevicePolicyManagerService.dumpApps(pw, "Packages subject to suspension",
+ getPersonalAppsForSuspension());
+
+ pw.decreaseIndent();
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index 9447f390ada0..8ef92393242a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -19,7 +19,7 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.verify.domain.DomainSet
import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationRequest
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Parcel
import android.os.Parcelable
import android.os.UserHandle
@@ -28,7 +28,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.UUID
-import kotlin.random.Random
@RunWith(Parameterized::class)
class DomainVerificationCoreApiTest {
@@ -92,9 +91,9 @@ class DomainVerificationCoreApiTest {
}
),
Parameter(
- testName = "DomainVerificationUserSelection",
+ testName = "DomainVerificationUserState",
initial = {
- DomainVerificationUserSelection(
+ DomainVerificationUserState(
UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
"com.test.pkg",
UserHandle.of(10),
@@ -103,22 +102,22 @@ class DomainVerificationCoreApiTest {
.associate { it.value to (it.index % 3) }
)
},
- unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
+ unparcel = { DomainVerificationUserState.CREATOR.createFromParcel(it) },
assertion = { first, second ->
- assertAll<DomainVerificationUserSelection, UUID>(first, second,
+ assertAll<DomainVerificationUserState, UUID>(first, second,
{ it.identifier }, { it.component1() }, IS_EQUAL_TO
)
- assertAll<DomainVerificationUserSelection, String>(first, second,
+ assertAll<DomainVerificationUserState, String>(first, second,
{ it.packageName }, { it.component2() }, IS_EQUAL_TO
)
- assertAll<DomainVerificationUserSelection, UserHandle>(first, second,
+ assertAll<DomainVerificationUserState, UserHandle>(first, second,
{ it.user }, { it.component3() }, IS_EQUAL_TO
)
- assertAll<DomainVerificationUserSelection, Boolean>(
+ assertAll<DomainVerificationUserState, Boolean>(
first, second, { it.isLinkHandlingAllowed },
{ it.component4() }, IS_EQUAL_TO
)
- assertAll<DomainVerificationUserSelection, Map<String, Int>>(
+ assertAll<DomainVerificationUserState, Map<String, Int>>(
first, second, { it.hostToStateMap },
{ it.component5() }, IS_MAP_EQUAL_TO
)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 89394837655a..53f0ca20e787 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -155,6 +155,15 @@ class DomainVerificationEnforcerTest {
assertApprovedVerifier(it.callingUid, it.proxy)
},
enforcer(
+ Type.SELECTION_QUERENT,
+ "approvedUserStateQuerent"
+ ) {
+ assertApprovedUserStateQuerent(
+ it.callingUid, it.callingUserId,
+ it.targetPackageName, it.userId
+ )
+ },
+ enforcer(
Type.SELECTOR,
"approvedUserSelector"
) {
@@ -170,7 +179,7 @@ class DomainVerificationEnforcerTest {
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "setUserSelectionInternal") {
+ service(Type.INTERNAL, "setUserStateInternal") {
setDomainVerificationUserSelectionInternal(
it.userId,
it.targetPackageName,
@@ -184,11 +193,11 @@ class DomainVerificationEnforcerTest {
service(Type.INTERNAL, "clearState") {
clearDomainVerificationState(listOf(it.targetPackageName))
},
- service(Type.INTERNAL, "clearUserSelections") {
- clearUserSelections(listOf(it.targetPackageName), it.userId)
+ service(Type.INTERNAL, "clearUserStates") {
+ clearUserStates(listOf(it.targetPackageName), it.userId)
},
- service(Type.VERIFIER, "getPackageNames") {
- validVerificationPackageNames
+ service(Type.VERIFIER, "queryValidPackageNames") {
+ queryValidVerificationPackageNames()
},
service(Type.QUERENT, "getInfo") {
getDomainVerificationInfo(it.targetPackageName)
@@ -208,26 +217,13 @@ class DomainVerificationEnforcerTest {
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.SELECTOR, "setLinkHandlingAllowed") {
- setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
- },
service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
},
- service(Type.SELECTOR, "getUserSelection") {
- getDomainVerificationUserSelection(it.targetPackageName)
- },
- service(Type.SELECTOR_USER, "getUserSelectionUserId") {
- getDomainVerificationUserSelection(it.targetPackageName, it.userId)
+ service(Type.SELECTION_QUERENT, "getUserStateUserId") {
+ getDomainVerificationUserState(it.targetPackageName, it.userId)
},
- service(Type.SELECTOR, "setUserSelection") {
- setDomainVerificationUserSelection(
- it.targetDomainSetId,
- setOf("example.com"),
- true
- )
- },
- service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+ service(Type.SELECTOR_USER, "setUserStateUserId") {
setDomainVerificationUserSelection(
it.targetDomainSetId,
setOf("example.com"),
@@ -244,10 +240,6 @@ class DomainVerificationEnforcerTest {
service(Type.LEGACY_QUERENT, "getLegacyUserState") {
getLegacyState(it.targetPackageName, it.userId)
},
- service(Type.OWNER_QUERENT, "getOwnersForDomain") {
- // Re-use package name, since the result itself isn't relevant
- getOwnersForDomain(it.targetPackageName)
- },
service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") {
// Re-use package name, since the result itself isn't relevant
getOwnersForDomain(it.targetPackageName, it.userId)
@@ -362,6 +354,7 @@ class DomainVerificationEnforcerTest {
Type.INTERNAL -> internal()
Type.QUERENT -> approvedQuerent()
Type.VERIFIER -> approvedVerifier()
+ Type.SELECTION_QUERENT -> approvedUserStateQuerent(verifyCrossUser = true)
Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
Type.LEGACY_QUERENT -> legacyQuerent()
@@ -371,7 +364,7 @@ class DomainVerificationEnforcerTest {
}.run { /*exhaust*/ }
}
- fun internal() {
+ private fun internal() {
val context: Context = mockThrowOnUnmocked()
val target = params.construct(context)
@@ -385,13 +378,13 @@ class DomainVerificationEnforcerTest {
}
}
- fun approvedQuerent() {
- val allowUserSelection = AtomicBoolean(false)
+ private fun approvedQuerent() {
+ val allowUserState = AtomicBoolean(false)
val allowPreferredApps = AtomicBoolean(false)
val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
initPermission(
- allowUserSelection,
+ allowUserState,
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
)
initPermission(
@@ -418,7 +411,7 @@ class DomainVerificationEnforcerTest {
assertFails { runMethod(target, NON_VERIFIER_UID) }
- allowUserSelection.set(true)
+ allowUserState.set(true)
assertFails { runMethod(target, NON_VERIFIER_UID) }
@@ -427,7 +420,7 @@ class DomainVerificationEnforcerTest {
runMethod(target, NON_VERIFIER_UID)
}
- fun approvedVerifier() {
+ private fun approvedVerifier() {
val allowDomainVerificationAgent = AtomicBoolean(false)
val allowIntentVerificationAgent = AtomicBoolean(false)
val allowQueryAll = AtomicBoolean(false)
@@ -469,12 +462,61 @@ class DomainVerificationEnforcerTest {
assertFails { runMethod(target, NON_VERIFIER_UID) }
}
- fun approvedUserSelector(verifyCrossUser: Boolean) {
- val allowUserSelection = AtomicBoolean(false)
+ private fun approvedUserStateQuerent(verifyCrossUser: Boolean) {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ }
+ val target = params.construct(context)
+
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // User selector makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ if (throws) {
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ // User selector doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
+ }
+ }
+
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ if (verifyCrossUser) {
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+ }
+
+ allowInteractAcrossUsers.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ if (verifyCrossUser) {
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+ }
+
+ private fun approvedUserSelector(verifyCrossUser: Boolean) {
+ val allowUserState = AtomicBoolean(false)
val allowInteractAcrossUsers = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
initPermission(
- allowUserSelection,
+ allowUserState,
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
)
initPermission(
@@ -515,7 +557,7 @@ class DomainVerificationEnforcerTest {
runTestCases(callingUserId, notCallingUserId, throws = true)
}
- allowUserSelection.set(true)
+ allowUserState.set(true)
runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
@@ -641,7 +683,7 @@ class DomainVerificationEnforcerTest {
private fun ownerQuerent(verifyCrossUser: Boolean) {
val allowQueryAll = AtomicBoolean(false)
- val allowUserSelection = AtomicBoolean(false)
+ val allowUserState = AtomicBoolean(false)
val allowInteractAcrossUsers = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
initPermission(
@@ -649,7 +691,7 @@ class DomainVerificationEnforcerTest {
android.Manifest.permission.QUERY_ALL_PACKAGES
)
initPermission(
- allowUserSelection,
+ allowUserState,
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
)
initPermission(
@@ -690,7 +732,7 @@ class DomainVerificationEnforcerTest {
runTestCases(callingUserId, notCallingUserId, throws = true)
}
- allowUserSelection.set(true)
+ allowUserState.set(true)
runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
@@ -769,6 +811,9 @@ class DomainVerificationEnforcerTest {
// INTERNAL || domain verification agent
VERIFIER,
+ // No permissions, allows all apps to view domain state for visible packages
+ SELECTION_QUERENT,
+
// Holding the user setting permission
SELECTOR,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index 439048ce51bb..8c31c65e1b0a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -18,7 +18,7 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationInfo
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
import com.android.server.pm.verify.domain.DomainVerificationPersistence
operator fun <F> android.util.Pair<F, *>.component1() = first
@@ -30,11 +30,11 @@ operator fun DomainVerificationInfo.component1() = identifier
operator fun DomainVerificationInfo.component2() = packageName
operator fun DomainVerificationInfo.component3() = hostToStateMap
-operator fun DomainVerificationUserSelection.component1() = identifier
-operator fun DomainVerificationUserSelection.component2() = packageName
-operator fun DomainVerificationUserSelection.component3() = user
-operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed
-operator fun DomainVerificationUserSelection.component5() = hostToStateMap
+operator fun DomainVerificationUserState.component1() = identifier
+operator fun DomainVerificationUserState.component2() = packageName
+operator fun DomainVerificationUserState.component3() = user
+operator fun DomainVerificationUserState.component4() = isLinkHandlingAllowed
+operator fun DomainVerificationUserState.component5() = hostToStateMap
operator fun DomainVerificationPersistence.ReadResult.component1() = active
operator fun DomainVerificationPersistence.ReadResult.component2() = restored
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
index a92ab9e35ddc..ad9aa7b6e3ae 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -22,9 +22,9 @@ import android.util.TypedXmlPullParser
import android.util.TypedXmlSerializer
import android.util.Xml
import com.android.server.pm.verify.domain.DomainVerificationPersistence
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
@@ -102,14 +102,14 @@ class DomainVerificationPersistenceTest {
// A domain without a written state falls back to default
stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
- userSelectionStates[1] = DomainVerificationUserState(1).apply {
+ userStates[1] = DomainVerificationInternalUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
isLinkHandlingAllowed = true
}
}
val stateOne = mockEmptyPkgState(1).apply {
// It's valid to have a user selection without any autoVerify domains
- userSelectionStates[1] = DomainVerificationUserState(1).apply {
+ userStates[1] = DomainVerificationInternalUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
isLinkHandlingAllowed = false
}
@@ -214,7 +214,7 @@ class DomainVerificationPersistenceTest {
private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply {
stateMap["$packageName.com"] = id
- userSelectionStates[id] = DomainVerificationUserState(id).apply {
+ userStates[id] = DomainVerificationInternalUserState(id).apply {
addHosts(setOf("$packageName-user.com"))
isLinkHandlingAllowed = true
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 010eacf3f51f..0d8f275be09c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -122,8 +122,8 @@ class DomainVerificationSettingsMutationTest {
service("clearState") {
clearDomainVerificationState(listOf(TEST_PKG))
},
- service("clearUserSelections") {
- clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+ service("clearUserStates") {
+ clearUserStates(listOf(TEST_PKG), TEST_USER_ID)
},
service("setStatus") {
setDomainVerificationStatus(
@@ -147,19 +147,13 @@ class DomainVerificationSettingsMutationTest {
DomainVerificationManager.STATE_SUCCESS
)
},
- service("setLinkHandlingAllowed") {
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
- },
service("setLinkHandlingAllowedUserId") {
setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
},
service("setLinkHandlingAllowedInternal") {
setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
},
- service("setUserSelection") {
- setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
- },
- service("setUserSelectionUserId") {
+ service("setUserStateUserId") {
setDomainVerificationUserSelection(
TEST_UUID,
setOf("example.com"),
@@ -167,7 +161,7 @@ class DomainVerificationSettingsMutationTest {
TEST_USER_ID
)
},
- service("setUserSelectionInternal") {
+ service("setUserStateInternal") {
setDomainVerificationUserSelectionInternal(
TEST_USER_ID,
TEST_PKG,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 48056a2b54d1..0576125748fb 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -16,18 +16,16 @@
package com.android.server.pm.test.verify.domain
-import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.verify.domain.DomainVerificationManager
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Build
import android.os.PatternMatcher
import android.os.Process
import android.util.ArraySet
-import androidx.test.InstrumentationRegistry
import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationService
@@ -41,7 +39,7 @@ import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import java.util.UUID
-class DomainVerificationManagerUserSelectionOverrideTest {
+class DomainVerificationUserStateOverrideTest {
companion object {
private const val PKG_ONE = "com.test.one"
@@ -50,17 +48,19 @@ class DomainVerificationManagerUserSelectionOverrideTest {
private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
private val DOMAIN_ONE =
- DomainVerificationManagerUserSelectionOverrideTest::class.java.packageName
+ DomainVerificationUserStateOverrideTest::class.java.packageName
- private const val STATE_NONE = DomainVerificationUserSelection.DOMAIN_STATE_NONE
- private const val STATE_SELECTED = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED
- private const val STATE_VERIFIED = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED
+ private const val STATE_NONE = DomainVerificationUserState.DOMAIN_STATE_NONE
+ private const val STATE_SELECTED = DomainVerificationUserState.DOMAIN_STATE_SELECTED
+ private const val STATE_VERIFIED = DomainVerificationUserState.DOMAIN_STATE_VERIFIED
+
+ private const val USER_ID = 0
}
private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
- fun makeManager(): DomainVerificationManager =
+ fun makeService() =
DomainVerificationService(mockThrowOnUnmocked {
// Assume the test has every permission necessary
whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
@@ -88,7 +88,7 @@ class DomainVerificationManagerUserSelectionOverrideTest {
addPackage(pkg2)
// Starting state for all tests is to have domain 1 enabled for the first package
- setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true)
+ setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true, USER_ID)
assertThat(stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
}
@@ -138,37 +138,37 @@ class DomainVerificationManagerUserSelectionOverrideTest {
@Test
fun anotherPackageTakeoverSuccess() {
- val manager = makeManager()
+ val service = makeService()
// Attempt override by package 2
- manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+ service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
// 1 loses approval
- assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE)
+ assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE)
// 2 gains approval
- assertThat(manager.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
+ assertThat(service.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
// 2 is the only owner
- assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+ assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
.containsExactly(PKG_TWO)
}
@Test(expected = IllegalArgumentException::class)
fun anotherPackageTakeoverFailure() {
- val manager = makeManager()
+ val service = makeService()
// Verify 1 to give it a higher approval level
- manager.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
+ service.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
DomainVerificationManager.STATE_SUCCESS)
- assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
- assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+ assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
+ assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
.containsExactly(PKG_ONE)
// Attempt override by package 2
- manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+ service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
}
- private fun DomainVerificationManager.stateFor(pkgName: String, host: String) =
- getDomainVerificationUserSelection(pkgName)!!.hostToStateMap[host]
+ private fun DomainVerificationService.stateFor(pkgName: String, host: String) =
+ getDomainVerificationUserState(pkgName, USER_ID)!!.hostToStateMap[host]
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 91098813380e..51c9b0ddb0d6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -85,6 +85,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
@@ -1649,8 +1650,8 @@ public class AlarmManagerServiceTest {
eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1669,8 +1670,8 @@ public class AlarmManagerServiceTest {
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1716,8 +1717,8 @@ public class AlarmManagerServiceTest {
isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE),
bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1742,8 +1743,8 @@ public class AlarmManagerServiceTest {
eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1772,8 +1773,8 @@ public class AlarmManagerServiceTest {
eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1797,8 +1798,8 @@ public class AlarmManagerServiceTest {
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@@ -1822,8 +1823,8 @@ public class AlarmManagerServiceTest {
eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
- final Bundle idleOptions = bundleCaptor.getValue();
- final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index f7f592886473..3870b02ba37c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -641,6 +641,6 @@ public class ConnectivityControllerTest {
private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
return new JobStatus(job.build(), uid, null, -1, 0, null,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 91b3cb7dbdd9..7925b69852ba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -685,7 +685,7 @@ public class JobStatusTest {
final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
return new JobStatus(job, 0, null, -1, 0, null, earliestRunTimeElapsedMillis,
- latestRunTimeElapsedMillis, 0, 0, null, 0);
+ latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
}
private static JobStatus createJobStatus(JobInfo job) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
new file mode 100644
index 000000000000..d786a5dac83a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mockitoSession;
+
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
+import android.content.Context;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.usage.UserUsageStatsService.StatsUpdatedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.util.HashMap;
+
+@RunWith(AndroidJUnit4.class)
+public class UserUsageStatsServiceTest {
+ private static final int TEST_USER_ID = 0;
+ private static final String TEST_PACKAGE_NAME = "test.package";
+ private static final long TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+ private UserUsageStatsService mService;
+ private MockitoSession mMockitoSession;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private StatsUpdatedListener mStatsUpdatedListener;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ File dir = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+ mService = new UserUsageStatsService(mContext, TEST_USER_ID, dir, mStatsUpdatedListener);
+
+ HashMap<String, Long> installedPkgs = new HashMap<>();
+ installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis());
+
+ mService.init(System.currentTimeMillis(), installedPkgs);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ }
+
+ @Test
+ public void testReportEvent_eventAppearsInQueries() {
+ Event event = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime());
+ event.mPackage = TEST_PACKAGE_NAME;
+ mService.reportEvent(event);
+
+ long now = System.currentTimeMillis();
+ long startTime = now - TIME_INTERVAL_MILLIS;
+ UsageEvents events = mService.queryEventsForPackage(
+ startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */);
+
+ boolean hasTestEvent = false;
+ while (events != null && events.hasNextEvent()) {
+ Event outEvent = new Event();
+ events.getNextEvent(outEvent);
+ if (outEvent.mEventType == ACTIVITY_RESUMED) {
+ hasTestEvent = true;
+ }
+ }
+ assertTrue(hasTestEvent);
+ }
+
+ @Test
+ public void testReportEvent_packageUsedEventNotTracked() {
+ Event event = new Event(APP_COMPONENT_USED, SystemClock.elapsedRealtime());
+ event.mPackage = TEST_PACKAGE_NAME;
+ mService.reportEvent(event);
+
+ long now = System.currentTimeMillis();
+ long startTime = now - TIME_INTERVAL_MILLIS;
+ UsageEvents events = mService.queryEventsForPackage(
+ startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */);
+
+ boolean hasTestEvent = false;
+ while (events != null && events.hasNextEvent()) {
+ Event outEvent = new Event();
+ events.getNextEvent(outEvent);
+ if (outEvent.mEventType == APP_COMPONENT_USED) {
+ hasTestEvent = true;
+ }
+ }
+ assertFalse(hasTestEvent);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index cfa2086793a4..f897d5ca3cc8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -160,6 +160,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
+ @Mock private AccessibilityTrace mMockA11yTrace;
@Mock private WindowManagerInternal mMockWindowManagerInternal;
@Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private IBinder mMockService;
@@ -188,6 +189,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
// Fake a11yWindowInfo and remote a11y connection for tests.
addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false, Display.DEFAULT_DISPLAY);
addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
@@ -227,8 +229,8 @@ public class AbstractAccessibilityServiceConnectionTest {
mServiceConnection = new TestAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
- mMockSystemSupport, mMockWindowManagerInternal, mMockSystemActionPerformer,
- mMockA11yWindowManager);
+ mMockSystemSupport, mMockA11yTrace, mMockWindowManagerInternal,
+ mMockSystemActionPerformer, mMockA11yWindowManager);
// Assume that the service is connected
mServiceConnection.mService = mMockService;
mServiceConnection.mServiceInterface = mMockServiceInterface;
@@ -849,12 +851,13 @@ public class AbstractAccessibilityServiceConnectionTest {
TestAccessibilityServiceConnection(Context context, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy,
- SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
+ SystemSupport systemSupport, AccessibilityTrace trace,
+ WindowManagerInternal windowManagerInternal,
SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
- a11yWindowManager);
+ securityPolicy, systemSupport, trace, windowManagerInternal,
+ systemActionPerfomer, a11yWindowManager);
mResolvedUserId = USER_ID;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 4b2a9fcd10d2..80e81d6e7cb9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -30,7 +30,6 @@ import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEA
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -51,16 +50,19 @@ import android.view.MotionEvent;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -91,7 +93,9 @@ public class AccessibilityInputFilterTest {
FullScreenMagnificationGestureHandler.class, TouchExplorer.class,
AutoclickController.class, AccessibilityInputFilter.class};
- private FullScreenMagnificationController mMockFullScreenMagnificationController;
+ @Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
+ @Mock private WindowManagerInternal mMockWindowManagerService;
+ @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
private AccessibilityManagerService mAms;
private AccessibilityInputFilter mA11yInputFilter;
private EventCaptor mCaptor1;
@@ -134,16 +138,21 @@ public class AccessibilityInputFilterTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(
+ WindowManagerInternal.class, mMockWindowManagerService);
+ when(mMockWindowManagerService.getAccessibilityController()).thenReturn(
+ mMockA11yController);
+ when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false);
setDisplayCount(1);
mAms = spy(new AccessibilityManagerService(context));
- mMockFullScreenMagnificationController = mock(FullScreenMagnificationController.class);
mA11yInputFilter = new AccessibilityInputFilter(context, mAms, mEventHandler);
mA11yInputFilter.onInstalled();
- when(mAms.getValidDisplayList()).thenReturn(mDisplayList);
- when(mAms.getFullScreenMagnificationController()).thenReturn(
- mMockFullScreenMagnificationController);
+ doReturn(mDisplayList).when(mAms).getValidDisplayList();
+ doReturn(mMockFullScreenMagnificationController).when(mAms)
+ .getFullScreenMagnificationController();
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 110bb21b5851..bcc756a0f8e8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -84,6 +84,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
@Mock private AccessibilityServiceInfo mMockServiceInfo;
@Mock private ResolveInfo mMockResolveInfo;
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
+ @Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
@Mock private PackageManager mMockPackageManager;
@Mock private WindowManagerInternal mMockWindowManagerService;
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@@ -115,6 +116,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
mMockWindowMagnificationMgr);
+ when(mMockWindowManagerService.getAccessibilityController()).thenReturn(
+ mMockA11yController);
+ when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false);
mA11yms = new AccessibilityManagerService(
InstrumentationRegistry.getContext(),
mMockPackageManager,
@@ -153,6 +157,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
new Object(),
mMockSecurityPolicy,
mMockSystemSupport,
+ mA11yms,
mMockWindowManagerService,
mMockSystemActionPerformer,
mMockA11yWindowManager,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 6963a1ab1538..00daa5c89fba 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -85,6 +85,7 @@ public class AccessibilityServiceConnectionTest {
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
+ @Mock AccessibilityTrace mMockA11yTrace;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
@@ -110,12 +111,13 @@ public class AccessibilityServiceConnectionTest {
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
+ when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
- mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
- mMockSystemActionPerformer, mMockA11yWindowManager,
- mMockActivityTaskManagerInternal);
+ mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
+ mMockWindowManagerInternal, mMockSystemActionPerformer,
+ mMockA11yWindowManager, mMockActivityTaskManagerInternal);
when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 8062bfec3703..160308762a58 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -63,6 +63,7 @@ public class UiAutomationManagerTest {
@Mock AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
+ @Mock AccessibilityTrace mMockA11yTrace;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock IBinder mMockOwner;
@@ -80,6 +81,7 @@ public class UiAutomationManagerTest {
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockAccessibilityServiceClient.asBinder()).thenReturn(mMockServiceAsBinder);
+ when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
final Context context = getInstrumentation().getTargetContext();
when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(
@@ -197,7 +199,7 @@ public class UiAutomationManagerTest {
private void register(int flags) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
- mMessageCapturingHandler, mMockSecurityPolicy, mMockSystemSupport,
+ mMessageCapturingHandler, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
mMockWindowManagerInternal, mMockSystemActionPerformer,
mMockA11yWindowManager, flags);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 92221c9713d3..bcd853c76a79 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -94,8 +94,7 @@ public class LogicalDisplayMapperTest {
// Disable binder caches in this process.
PropertyInvalidatedCache.disableForTestMode();
- mLogicalDisplayMapper = new LogicalDisplayMapper(
- mContext, mDisplayDeviceRepo, mListenerMock);
+ mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo, mListenerMock);
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index deaeb46c4074..8b35af80e47f 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -276,7 +276,7 @@ public class JobStoreTest {
0 /* sourceUserId */, 0, "someTag",
invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
- persistedExecutionTimesUTC, 0 /* innerFlagg */);
+ persistedExecutionTimesUTC, 0 /* innerFlag */, 0 /* dynamicConstraints */);
mTaskStoreUnderTest.add(js);
waitForPendingIo();
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index f87d5993c1b5..7d9ab3772733 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -20,8 +20,10 @@ import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static com.android.server.job.JobConcurrencyManager.workTypeToString;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -54,7 +56,7 @@ public class WorkCountTrackerTest {
private static final double[] EQUAL_PROBABILITY_CDF =
buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
- 1.0 / NUM_WORK_TYPES);
+ 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES);
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -66,8 +68,9 @@ public class WorkCountTrackerTest {
}
@NonNull
- private static double[] buildWorkTypeCdf(double pTop, double pEj, double pBg, double pBgUser) {
- return buildCdf(pTop, pEj, pBg, pBgUser);
+ private static double[] buildWorkTypeCdf(
+ double pTop, double pFgs, double pEj, double pBg, double pBgUser) {
+ return buildCdf(pTop, pFgs, pEj, pBg, pBgUser);
}
@NonNull
@@ -102,10 +105,12 @@ public class WorkCountTrackerTest {
case 0:
return WORK_TYPE_TOP;
case 1:
- return WORK_TYPE_EJ;
+ return WORK_TYPE_FGS;
case 2:
- return WORK_TYPE_BG;
+ return WORK_TYPE_EJ;
case 3:
+ return WORK_TYPE_BG;
+ case 4:
return WORK_TYPE_BGUSER;
default:
throw new IllegalStateException("Unknown work type");
@@ -224,12 +229,15 @@ public class WorkCountTrackerTest {
private void startPendingJobs(Jobs jobs) {
while (hasStartablePendingJob(jobs)) {
- final int startingWorkType =
- getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+ final int workType = getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+ if (jobs.pending.get(workType) > 0) {
+ final int pendingMultiType = getPendingMultiType(jobs, workType);
+ final int startingWorkType = mWorkCountTracker.canJobStart(pendingMultiType);
+ if (startingWorkType == WORK_TYPE_NONE) {
+ continue;
+ }
- if (jobs.pending.get(startingWorkType) > 0
- && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
- final int pendingMultiType = getPendingMultiType(jobs, startingWorkType);
jobs.removePending(pendingMultiType);
jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
mWorkCountTracker.stageJob(startingWorkType, pendingMultiType);
@@ -304,7 +312,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0);
+ final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -322,7 +330,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.75, .2, .05);
final double probStart = 0.5;
@@ -340,7 +348,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.05, .95);
final double probStart = 0.5;
@@ -358,7 +366,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.8, .1);
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, .1);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -376,7 +384,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.1, 0);
+ final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -394,7 +402,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.1, .8);
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, .8);
final double[] numTypesCdf = buildCdf(0.5, 0.5);
final double probStart = 0.5;
@@ -413,7 +421,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.05, 0.05);
+ final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -432,7 +440,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.5, 0.5);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0.5);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -451,7 +459,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.1, 0.9);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0.9);
final double[] numTypesCdf = buildCdf(0.9, 0.1);
final double probStart = 0.5;
@@ -470,7 +478,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.9, 0.1);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0.1);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -488,7 +496,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.5, 0.5, 0, 0);
+ final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0);
final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
final double probStart = 0.5;
@@ -511,7 +519,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
final double probStop = 0.13;
- final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.85);
+ final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.8, 0.05);
final double probStart = 0.87;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
@@ -528,7 +536,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(.1, 0.5, 0.35, 0.05);
+ final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -548,7 +556,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.01, 0.49, 0.1, 0.4);
+ final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0.4);
final double[] numTypesCdf = buildCdf(0.7, 0.3);
final double probStart = 0.5;
@@ -576,11 +584,13 @@ public class WorkCountTrackerTest {
startPendingJobs(jobs);
for (Pair<Integer, Integer> run : resultRunning) {
- assertWithMessage("Incorrect running result for work type " + run.first)
+ assertWithMessage(
+ "Incorrect running result for work type " + workTypeToString(run.first))
.that(jobs.running.get(run.first)).isEqualTo(run.second);
}
for (Pair<Integer, Integer> pend : resultPending) {
- assertWithMessage("Incorrect pending result for work type " + pend.first)
+ assertWithMessage(
+ "Incorrect pending result for work type " + workTypeToString(pend.first))
.that(jobs.pending.get(pend.first)).isEqualTo(pend.second);
}
}
@@ -938,10 +948,15 @@ public class WorkCountTrackerTest {
assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
- assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+ // If run the TOP jobs as TOP first, and a TOP|EJ job as EJ, then we'll have 4 TOP jobs
+ // remaining.
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtLeast(4);
+ // If we end up running the TOP|EJ jobs as TOP first, then we'll have 5 TOP jobs remaining.
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtMost(5);
// Can't equate pending EJ since some could be running as TOP and BG
assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
- assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(8);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(9);
// Stop all jobs
jobs.maybeFinishJobs(1);
@@ -975,7 +990,7 @@ public class WorkCountTrackerTest {
assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
- assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(1);
assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(4);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index 2288a8925561..cc18317d0529 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -18,7 +18,9 @@ package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static com.android.server.job.JobConcurrencyManager.workTypeToString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -44,10 +46,12 @@ import java.util.List;
public class WorkTypeConfigTest {
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+ private static final String KEY_MAX_FGS = "concurrency_max_fgs_test";
private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+ private static final String KEY_MIN_FGS = "concurrency_min_fgs_test";
private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@@ -59,15 +63,17 @@ public class WorkTypeConfigTest {
private void resetConfig() {
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
}
private void check(@Nullable DeviceConfig.Properties config,
@@ -103,10 +109,12 @@ public class WorkTypeConfigTest {
assertEquals(expectedTotal, counts.getMaxTotal());
for (Pair<Integer, Integer> min : expectedMinLimits) {
- assertEquals((int) min.second, counts.getMinReserved(min.first));
+ assertEquals("Incorrect min value for " + workTypeToString(min.first),
+ (int) min.second, counts.getMinReserved(min.first));
}
for (Pair<Integer, Integer> max : expectedMaxLimits) {
- assertEquals((int) max.second, counts.getMax(max.first));
+ assertEquals("Incorrect max value for " + workTypeToString(max.first),
+ (int) max.second, counts.getMax(max.first));
}
}
@@ -193,6 +201,14 @@ public class WorkTypeConfigTest {
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3)),
+ /*expected*/ true, 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3)));
check(null, /*default*/ 15,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
@@ -289,5 +305,30 @@ public class WorkTypeConfigTest {
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 16)
+ .setInt(KEY_MAX_TOP, 16)
+ .setInt(KEY_MIN_TOP, 1)
+ .setInt(KEY_MAX_FGS, 15)
+ .setInt(KEY_MIN_FGS, 2)
+ .setInt(KEY_MAX_EJ, 14)
+ .setInt(KEY_MIN_EJ, 3)
+ .setInt(KEY_MAX_BG, 13)
+ .setInt(KEY_MIN_BG, 4)
+ .setInt(KEY_MAX_BGUSER, 12)
+ .setInt(KEY_MIN_BGUSER, 5)
+ .build(),
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /* max */
+ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15),
+ Pair.create(WORK_TYPE_EJ, 14),
+ Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 12)));
}
}
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 137cf6523caf..09a436c59e7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -518,6 +518,7 @@ public class DisplayContentTests extends WindowTestsBase {
TYPE_WALLPAPER, TYPE_APPLICATION);
final WindowState wallpaper = windows[0];
assertTrue(wallpaper.mIsWallpaper);
+ wallpaper.mToken.asWallpaperToken().setVisibility(false);
// By default WindowState#mWallpaperVisible is false.
assertFalse(wallpaper.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index d663b649fbba..cc1869e72b34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -454,7 +454,7 @@ public class LockTaskControllerTest {
Settings.Secure.clearProviderForTest();
// AND a password is set
- when(mLockPatternUtils.isSecure(anyInt()))
+ when(mLockPatternUtils.isSecure(TEST_USER_ID))
.thenReturn(true);
// AND there is a task record
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index fb2272ed9fd3..5239462a1ec0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -473,6 +473,68 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
mDefaultDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
}
+ @Test
+ public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea() {
+ final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FREEFORM);
+ final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
+ mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+ secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
+ DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
+ final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ launchRoot.mCreatedByOrganizer = true;
+ secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
+ new int[] { ACTIVITY_TYPE_STANDARD });
+ final Rect secondaryDAStableBounds = new Rect();
+ secondaryDisplayArea.getStableRect(secondaryDAStableBounds);
+
+ // Specify the display and provide a layout so that it will be set to freeform bounds.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(freeformDisplay.getDisplayId());
+ final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+ .setGravity(Gravity.LEFT).build();
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setOptions(options).setLayout(layout).calculate());
+
+ assertEquals(secondaryDisplayArea, mResult.mPreferredTaskDisplayArea);
+ assertTrue(secondaryDAStableBounds.contains(mResult.mBounds));
+ }
+
+ @Test
+ public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea_unresizableApp() {
+ mAtm.mSupportsNonResizableMultiWindow = true;
+
+ final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FREEFORM);
+ final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
+ mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+ secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
+ DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
+ final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ launchRoot.mCreatedByOrganizer = true;
+ secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
+ new int[] { ACTIVITY_TYPE_STANDARD });
+ final Rect secondaryDAStableBounds = new Rect();
+ secondaryDisplayArea.getStableRect(secondaryDAStableBounds);
+
+ // The bounds will get updated for unresizable with opposite orientation on freeform display
+ final Rect displayBounds = new Rect(freeformDisplay.getBounds());
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.screenOrientation = displayBounds.width() > displayBounds.height()
+ ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE;
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(freeformDisplay.getDisplayId());
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setOptions(options).calculate());
+
+ assertEquals(secondaryDisplayArea, mResult.mPreferredTaskDisplayArea);
+ assertTrue(secondaryDAStableBounds.contains(mResult.mBounds));
+ }
+
// =====================================
// Launch Windowing Mode Related Tests
// =====================================
@@ -1365,8 +1427,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
// This test case requires a relatively big app bounds to ensure the default size calculated
// by letterbox won't be too small to hold the minimum width/height.
configInsetsState(
- freeformDisplay.getInsetsStateController().getRawInsetsState(),
- DISPLAY_BOUNDS, new Rect(10, 10, 1910, 1070));
+ freeformDisplay.getInsetsStateController().getRawInsetsState(), freeformDisplay,
+ new Rect(10, 10, 1910, 1070));
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
@@ -1587,15 +1649,17 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
display.setBounds(DISPLAY_BOUNDS);
display.getConfiguration().densityDpi = DENSITY_DEFAULT;
display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
- configInsetsState(display.getInsetsStateController().getRawInsetsState(),
- DISPLAY_BOUNDS, DISPLAY_STABLE_BOUNDS);
+ configInsetsState(display.getInsetsStateController().getRawInsetsState(), display,
+ DISPLAY_STABLE_BOUNDS);
return display;
}
/**
* Creates insets sources so that we can get the expected stable frame.
*/
- private static void configInsetsState(InsetsState state, Rect displayFrame, Rect stableFrame) {
+ private static void configInsetsState(InsetsState state, DisplayContent display,
+ Rect stableFrame) {
+ final Rect displayFrame = display.getBounds();
final int dl = displayFrame.left;
final int dt = displayFrame.top;
final int dr = displayFrame.right;
@@ -1618,6 +1682,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
if (sb < db) {
state.getSource(ITYPE_NAVIGATION_BAR).setFrame(dl, sb, dr, db);
}
+ // Recompute config and push to children.
+ display.onRequestedOverrideConfigurationChanged(display.getConfiguration());
}
private ActivityRecord createSourceActivity(TestDisplayContent display) {
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 401ace03c554..154a899fb5ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -316,9 +316,9 @@ public class TransitionTests extends WindowTestsBase {
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
"wallpaperWindow");
- wallpaperWindow.mWallpaperVisible = false;
+ wallpaperWindowToken.setVisibleRequested(false);
transition.collect(wallpaperWindowToken);
- wallpaperWindow.mWallpaperVisible = true;
+ wallpaperWindowToken.setVisibleRequested(true);
wallpaperWindow.mHasSurface = true;
// doesn't matter which order collected since participants is a set
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index d1d0ac68017a..8b4e94724e6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -24,6 +24,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -49,7 +51,9 @@ import android.view.Gravity;
import android.view.InsetsState;
import android.view.RoundedCorners;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.window.ITransitionPlayer;
import androidx.test.filters.SmallTest;
@@ -135,7 +139,8 @@ public class WallpaperControllerTests extends WindowTestsBase {
int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight));
// Check that the wallpaper is correctly scaled
- assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrame());
+ assertEquals(expectedWidth, wallpaperWindow.getFrame().width());
+ assertEquals(displayHeight, wallpaperWindow.getFrame().height());
Rect portraitFrame = wallpaperWindow.getFrame();
// Rotate the display
@@ -297,6 +302,46 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertFalse(mAppWindow.mActivityRecord.hasFixedRotationTransform());
}
+ @Test
+ public void testWallpaperTokenVisibility() {
+ final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+ final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, dc, true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, token,
+ "wallpaperWindow");
+ wallpaperWindow.setHasSurface(true);
+
+ // Set-up mock shell transitions
+ final IBinder mockBinder = mock(IBinder.class);
+ final ITransitionPlayer mockPlayer = mock(ITransitionPlayer.class);
+ doReturn(mockBinder).when(mockPlayer).asBinder();
+ mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer);
+
+ Transition transit =
+ mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
+
+ // wallpaper windows are immediately visible when set to visible even during a transition
+ token.setVisibility(true);
+ assertTrue(wallpaperWindow.isVisible());
+ assertTrue(token.isVisibleRequested());
+ assertTrue(token.isVisible());
+ mWm.mAtmService.getTransitionController().abort(transit);
+
+ // In a transition, setting invisible should ONLY set requestedVisible false; otherwise
+ // wallpaper should remain "visible" until transition is over.
+ transit = mWm.mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE);
+ transit.start();
+ token.setVisibility(false);
+ assertTrue(wallpaperWindow.isVisible());
+ assertFalse(token.isVisibleRequested());
+ assertTrue(token.isVisible());
+
+ transit.onTransactionReady(transit.getSyncId(), mock(SurfaceControl.Transaction.class));
+ transit.finishTransition();
+ assertFalse(wallpaperWindow.isVisible());
+ assertFalse(token.isVisible());
+ }
+
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setTask(dc.getDefaultTaskDisplayArea().getRootHomeTask())
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 99c96bd0de1b..bbb885eb0dd0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -984,6 +984,22 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
+ public void testFreezeInsets() {
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, stack);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+
+ // Set visibility to false, verify the main window of the task will be set the frozen
+ // insets state immediately.
+ activity.setVisibility(false);
+ assertNotNull(win.getFrozenInsetsState());
+
+ // Now make it visible again, verify that the insets are immediately unfrozen.
+ activity.setVisibility(true);
+ assertNull(win.getFrozenInsetsState());
+ }
+
+ @Test
public void testFreezeInsetsStateWhenAppTransition() {
final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -996,15 +1012,20 @@ public class WindowContainerTests extends WindowTestsBase {
sources.add(activity);
// Simulate the task applying the exit transition, verify the main window of the task
- // will be set the frozen insets state.
+ // will be set the frozen insets state before the animation starts
+ activity.setVisibility(false);
task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
false /* isVoiceInteraction */, sources);
verify(win).freezeInsetsState();
- // Simulate the task transition finished, verify the frozen insets state of the window
- // will be reset.
+ // Simulate the task transition finished.
+ activity.commitVisibility(false, false);
task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
task.mSurfaceAnimator.getAnimation());
+
+ // Now make it visible again, verify that the insets are immediately unfrozen even before
+ // transition starts.
+ activity.setVisibility(true);
verify(win).clearFrozenInsetsState();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ebc5c4ff280a..1f38f463a7d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -245,6 +245,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
private WindowToken createWindowToken(
DisplayContent dc, int windowingMode, int activityType, int type) {
+ if (type == TYPE_WALLPAPER) {
+ return createWallpaperToken(dc);
+ }
if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
return createTestWindowToken(type, dc);
}
@@ -252,6 +255,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
return createActivityRecord(dc, windowingMode, activityType);
}
+ private WindowToken createWallpaperToken(DisplayContent dc) {
+ return new WallpaperWindowToken(mWm, mock(IBinder.class), true /* explicit */, dc,
+ true /* ownerCanManageAppTokens */);
+ }
+
WindowState createAppWindow(Task task, int type, String name) {
final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
task.addChild(activity, 0);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b1e6683f0486..f35b9e2ce0ed 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -303,7 +303,9 @@ class UserUsageStatsService {
// FLUSH_TO_DISK is a private event.
&& event.mEventType != Event.FLUSH_TO_DISK
// DEVICE_SHUTDOWN is added to event list after reboot.
- && event.mEventType != Event.DEVICE_SHUTDOWN) {
+ && event.mEventType != Event.DEVICE_SHUTDOWN
+ // We aren't interested in every instance of the APP_COMPONENT_USED event.
+ && event.mEventType != Event.APP_COMPONENT_USED) {
currentDailyStats.addEvent(event);
}
@@ -1176,6 +1178,8 @@ class UserUsageStatsService {
return "USER_STOPPED";
case Event.LOCUS_ID_SET:
return "LOCUS_ID_SET";
+ case Event.APP_COMPONENT_USED:
+ return "APP_COMPONENT_USED";
default:
return "UNKNOWN_TYPE_" + eventType;
}
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index bdf628b4d339..cedf48b0b8e1 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -24,9 +24,14 @@ import android.net.Uri;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -39,6 +44,8 @@ import java.util.List;
@SystemApi
public final class RcsContactPresenceTuple implements Parcelable {
+ private static final String LOG_TAG = "RcsContactPresenceTuple";
+
/**
* The service ID used to indicate that service discovery via presence is available.
* <p>
@@ -370,7 +377,8 @@ public final class RcsContactPresenceTuple implements Parcelable {
}
/**
- * The optional SIP Contact URI associated with the PIDF tuple element.
+ * The optional SIP Contact URI associated with the PIDF tuple element if the network
+ * expects the user to use the URI instead of the contact URI to contact it.
*/
public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
mPresenceTuple.mContactUri = contactUri;
@@ -381,8 +389,24 @@ public final class RcsContactPresenceTuple implements Parcelable {
* The optional timestamp indicating the data and time of the status change of this tuple.
* Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
* string per RFC3339.
+ * @hide
*/
public @NonNull Builder setTimestamp(@NonNull String timestamp) {
+ try {
+ mPresenceTuple.mTimestamp =
+ DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+ } catch (DateTimeParseException e) {
+ Log.d(LOG_TAG, "Parse timestamp failed " + e);
+ }
+ return this;
+ }
+
+ /**
+ * The optional timestamp indicating the data and time of the status change of this tuple.
+ * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+ * string per RFC3339.
+ */
+ public @NonNull Builder setTime(@NonNull Instant timestamp) {
mPresenceTuple.mTimestamp = timestamp;
return this;
}
@@ -414,7 +438,7 @@ public final class RcsContactPresenceTuple implements Parcelable {
}
private Uri mContactUri;
- private String mTimestamp;
+ private Instant mTimestamp;
private @BasicStatus String mStatus;
// The service information in the service-description element.
@@ -433,7 +457,7 @@ public final class RcsContactPresenceTuple implements Parcelable {
private RcsContactPresenceTuple(Parcel in) {
mContactUri = in.readParcelable(Uri.class.getClassLoader());
- mTimestamp = in.readString();
+ mTimestamp = convertStringFormatTimeToInstant(in.readString());
mStatus = in.readString();
mServiceId = in.readString();
mServiceVersion = in.readString();
@@ -444,7 +468,7 @@ public final class RcsContactPresenceTuple implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mContactUri, flags);
- out.writeString(mTimestamp);
+ out.writeString(convertInstantToStringFormat(mTimestamp));
out.writeString(mStatus);
out.writeString(mServiceId);
out.writeString(mServiceVersion);
@@ -470,6 +494,26 @@ public final class RcsContactPresenceTuple implements Parcelable {
}
};
+ // Convert the Instant to the string format
+ private String convertInstantToStringFormat(Instant instant) {
+ if (instant == null) {
+ return "";
+ }
+ return instant.toString();
+ }
+
+ // Convert the time string format to Instant
+ private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) {
+ if (TextUtils.isEmpty(timestamp)) {
+ return null;
+ }
+ try {
+ return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+ } catch (DateTimeParseException e) {
+ return null;
+ }
+ }
+
/** @return the status of the tuple element. */
public @NonNull @BasicStatus String getStatus() {
return mStatus;
@@ -490,8 +534,16 @@ public final class RcsContactPresenceTuple implements Parcelable {
return mContactUri;
}
- /** @return the timestamp element contained in the tuple if it exists */
+ /**
+ * @return the timestamp element contained in the tuple if it exists
+ * @hide
+ */
public @Nullable String getTimestamp() {
+ return (mTimestamp == null) ? null : mTimestamp.toString();
+ }
+
+ /** @return the timestamp element contained in the tuple if it exists */
+ public @Nullable Instant getTime() {
return mTimestamp;
}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 9299fed1e27d..52d0f036788c 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -340,6 +340,7 @@ public final class RcsContactUceCapability implements Parcelable {
}
/**
+ * Retrieve the contact URI requested by the applications.
* @return the URI representing the contact associated with the capabilities.
*/
public @NonNull Uri getContactUri() {
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 09c07d3f203c..815c08d120c2 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -32,11 +32,12 @@ import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -431,13 +432,15 @@ public class RcsUceAdapter {
/**
* The pending request has completed successfully due to all requested contacts information
- * being delivered.
+ * being delivered. The callback {@link #onCapabilitiesReceived(List)}
+ * for each contacts is required to be called before {@link #onComplete} is called.
*/
void onComplete();
/**
* The pending request has resulted in an error and may need to be retried, depending on the
- * error code.
+ * error code. The callback {@link #onCapabilitiesReceived(List)}
+ * for each contacts is required to be called before {@link #onError} is called.
* @param errorCode The reason for the framework being unable to process the request.
* @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
@@ -484,7 +487,6 @@ public class RcsUceAdapter {
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
- @SystemApi
@RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
Manifest.permission.READ_CONTACTS})
public void requestCapabilities(@NonNull List<Uri> contactNumbers,
@@ -550,6 +552,94 @@ public class RcsUceAdapter {
}
/**
+ * Request the User Capability Exchange capabilities for one or more contacts.
+ * <p>
+ * This will return the cached capabilities of the contact and will not perform a capability
+ * poll on the network unless there are contacts being queried with stale information.
+ * <p>
+ * Be sure to check the availability of this feature using
+ * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+ * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
+ * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+ *
+ * @param contactNumbers A list of numbers that the capabilities are being requested for.
+ * @param executor The executor that will be used when the request is completed and the
+ * {@link CapabilitiesCallback} is called.
+ * @param c A one-time callback for when the request for capabilities completes or there is an
+ * error processing the request.
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+ Manifest.permission.READ_CONTACTS})
+ public void requestCapabilities(@NonNull Collection<Uri> contactNumbers,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CapabilitiesCallback c) throws ImsException {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ if (contactNumbers == null) {
+ throw new IllegalArgumentException("Must include non-null contact number list.");
+ }
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "requestCapabilities: IImsRcsController is null");
+ throw new ImsException("Can not find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
+ @Override
+ public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ @Override
+ public void onComplete() {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> c.onComplete());
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ @Override
+ public void onError(int errorCode, long retryAfterMilliseconds) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ };
+
+ try {
+ imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback);
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.toString(), e.errorCode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
+ throw new ImsException("Remote IMS Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
* Ignore the device cache and perform a capability discovery for one contact, also called
* "availability fetch."
* <p>
@@ -570,6 +660,10 @@ public class RcsUceAdapter {
* {@link CapabilitiesCallback} is called.
* @param c A one-time callback for when the request for capabilities completes or there is
* an error processing the request.
+ * @throws ImsException if the subscription associated with this instance of
+ * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+ * available. This can happen if the ImsService has crashed, for example, or if the subscription
+ * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 908869beb607..00c91681d9ea 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -31,6 +31,7 @@ import android.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -241,7 +242,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Notify the framework of the response to the SUBSCRIBE request from
- * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)}.
+ * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
* <p>
* If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
* framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
@@ -266,7 +267,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Notify the framework of the response to the SUBSCRIBE request from
- * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)} that also
+ * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also
* includes a reason provided in the “reason” header. See RFC3326 for more
* information.
*
@@ -388,6 +389,7 @@ public class RcsCapabilityExchangeImplBase {
* @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
* capabilities for.
* @param cb The callback of the subscribe request.
+ * @hide
*/
// executor used is defined in the constructor.
@SuppressLint("ExecutorRegistration")
@@ -403,6 +405,40 @@ public class RcsCapabilityExchangeImplBase {
}
/**
+ * The user capabilities of one or multiple contacts have been requested by the framework.
+ * <p>
+ * The implementer must follow up this call with an
+ * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
+ * The response from the network to the SUBSCRIBE request must be sent back to the framework
+ * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+ * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+ * sent back to the framework using
+ * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+ * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
+ * should be called with the presence information for the contacts specified.
+ * <p>
+ * Once the subscription is terminated,
+ * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+ * framework to finish listening for NOTIFY responses.
+ *
+ * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
+ * UCE capabilities for.
+ * @param cb The callback of the subscribe request.
+ */
+ // executor used is defined in the constructor.
+ @SuppressLint("ExecutorRegistration")
+ public void subscribeForCapabilities(@NonNull Collection<Uri> uris,
+ @NonNull SubscribeResponseCallback cb) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
+ try {
+ cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+ } catch (ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
+
+ /**
* The capabilities of this device have been updated and should be published to the network.
* <p>
* If this operation succeeds, network response updates should be sent to the framework using
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 15d19a49ee56..541292a1e230 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -114,6 +114,7 @@ public class DctConstants {
public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54;
public static final int EVENT_SIM_STATE_UPDATED = BASE + 55;
public static final int EVENT_APN_UNTHROTTLED = BASE + 56;
+ public static final int EVENT_AIRPLANE_MODE_CHANGED = BASE + 57;
/***** Constants *****/
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
new file mode 100644
index 000000000000..2e985fbba269
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.test.input
+
+import android.view.InputDevice.SOURCE_MOUSE
+import android.view.InputDevice.SOURCE_TOUCHSCREEN
+import android.view.InputEventAssigner
+import android.view.KeyEvent
+import android.view.MotionEvent
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Create a MotionEvent with the provided action, eventTime, and source
+ */
+fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
+ val downTime: Long = 10
+ val x = 1f
+ val y = 2f
+ val pressure = 3f
+ val size = 1f
+ val metaState = 0
+ val xPrecision = 0f
+ val yPrecision = 0f
+ val deviceId = 1
+ val edgeFlags = 0
+ val displayId = 0
+ return MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
+ xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
+}
+
+fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+ val code = KeyEvent.KEYCODE_A
+ val repeat = 0
+ return KeyEvent(eventTime, eventTime, action, code, repeat)
+}
+
+class InputEventAssignerTest {
+ companion object {
+ private const val TAG = "InputEventAssignerTest"
+ }
+
+ /**
+ * A single MOVE event should be assigned to the next available frame.
+ */
+ @Test
+ fun testTouchGesture() {
+ val assigner = InputEventAssigner()
+ val event = createMotionEvent(MotionEvent.ACTION_MOVE, 10, SOURCE_TOUCHSCREEN)
+ val eventId = assigner.processEvent(event)
+ assertEquals(event.id, eventId)
+ }
+
+ /**
+ * DOWN event should be used until a vsync comes in. After vsync, the latest event should be
+ * produced.
+ */
+ @Test
+ fun testTouchDownWithMove() {
+ val assigner = InputEventAssigner()
+ val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_TOUCHSCREEN)
+ val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_TOUCHSCREEN)
+ val move2 = createMotionEvent(MotionEvent.ACTION_MOVE, 13, SOURCE_TOUCHSCREEN)
+ val move3 = createMotionEvent(MotionEvent.ACTION_MOVE, 14, SOURCE_TOUCHSCREEN)
+ val move4 = createMotionEvent(MotionEvent.ACTION_MOVE, 15, SOURCE_TOUCHSCREEN)
+ var eventId = assigner.processEvent(down)
+ assertEquals(down.id, eventId)
+ eventId = assigner.processEvent(move1)
+ assertEquals(down.id, eventId)
+ eventId = assigner.processEvent(move2)
+ // Even though we already had 2 move events, there was no choreographer callback yet.
+ // Therefore, we should still get the id of the down event
+ assertEquals(down.id, eventId)
+
+ // Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event
+ assigner.onChoreographerCallback()
+ eventId = assigner.processEvent(move3)
+ assertEquals(move3.id, eventId)
+ eventId = assigner.processEvent(move4)
+ assertEquals(move4.id, eventId)
+ }
+
+ /**
+ * Similar to the above test, but with SOURCE_MOUSE. Since we don't have down latency
+ * concept for non-touchscreens, the latest input event will be used.
+ */
+ @Test
+ fun testMouseDownWithMove() {
+ val assigner = InputEventAssigner()
+ val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_MOUSE)
+ val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_MOUSE)
+ var eventId = assigner.processEvent(down)
+ assertEquals(down.id, eventId)
+ eventId = assigner.processEvent(move1)
+ assertEquals(move1.id, eventId)
+ }
+
+ /**
+ * KeyEvents are processed immediately, so the latest event should be returned.
+ */
+ @Test
+ fun testKeyEvent() {
+ val assigner = InputEventAssigner()
+ val down = createKeyEvent(KeyEvent.ACTION_DOWN, 20)
+ var eventId = assigner.processEvent(down)
+ assertEquals(down.id, eventId)
+ val up = createKeyEvent(KeyEvent.ACTION_UP, 21)
+ eventId = assigner.processEvent(up)
+ // DOWN is only sticky for Motions, not for keys
+ assertEquals(up.id, eventId)
+ assigner.onChoreographerCallback()
+ val down2 = createKeyEvent(KeyEvent.ACTION_DOWN, 22)
+ eventId = assigner.processEvent(down2)
+ assertEquals(down2.id, eventId)
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
index c19e5cc34611..c01d32bf4cd2 100644
--- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -17,6 +17,7 @@
package com.android.test.input
import android.graphics.FrameInfo
+import android.os.IInputConstants.INVALID_INPUT_EVENT_ID
import android.os.SystemClock
import android.view.ViewFrameInfo
import com.google.common.truth.Truth.assertThat
@@ -33,8 +34,7 @@ class ViewFrameInfoTest {
@Before
fun setUp() {
mViewFrameInfo.reset()
- mViewFrameInfo.updateOldestInputEvent(10)
- mViewFrameInfo.updateNewestInputEvent(20)
+ mViewFrameInfo.setInputEvent(139)
mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
mTimeStarted = SystemClock.uptimeNanos()
mViewFrameInfo.markDrawStart()
@@ -43,8 +43,6 @@ class ViewFrameInfoTest {
@Test
fun testPopulateFields() {
assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted)
- assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10)
- assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20)
assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
}
@@ -53,8 +51,6 @@ class ViewFrameInfoTest {
mViewFrameInfo.reset()
// Ensure that the original object is reset correctly
assertThat(mViewFrameInfo.drawStart).isEqualTo(0)
- assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0)
- assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0)
assertThat(mViewFrameInfo.flags).isEqualTo(0)
}
@@ -62,12 +58,13 @@ class ViewFrameInfoTest {
fun testUpdateFrameInfoFromViewFrameInfo() {
val frameInfo = FrameInfo()
// By default, all values should be zero
- // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID
+ assertThat(frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID]).isEqualTo(INVALID_INPUT_EVENT_ID)
assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0)
assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0)
// The values inside FrameInfo should match those from ViewFrameInfo after we update them
mViewFrameInfo.populateFrameInfo(frameInfo)
+ assertThat(frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID]).isEqualTo(139)
assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index 7a60cc105a26..4cdf6a2a4b36 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -24,7 +24,6 @@ import android.os.RemoteException;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -54,12 +53,6 @@ public class CaptivePortalTest {
public void appRequest(final int request) throws RemoteException {
mCode = request;
}
-
- @Override
- public void logEvent(int eventId, String packageName) throws RemoteException {
- mCode = eventId;
- mPackageName = packageName;
- }
}
private interface TestFunctor {
@@ -98,12 +91,14 @@ public class CaptivePortalTest {
assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
}
+ /**
+ * Test testLogEvent is expected to do nothing but shouldn't crash, because the API logEvent
+ * has been deprecated.
+ */
@Test
public void testLogEvent() {
final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
- MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+ 0,
TEST_PACKAGE_NAME));
- assertEquals(result.mCode, MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
- assertEquals(result.mPackageName, TEST_PACKAGE_NAME);
}
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index c10c573aa024..2a2dc5628ecd 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -16,6 +16,7 @@
package com.android.server.net.integrationtests
+import android.app.usage.NetworkStatsManager
import android.content.ComponentName
import android.content.Context
import android.content.Context.BIND_AUTO_CREATE
@@ -25,7 +26,6 @@ import android.content.ServiceConnection
import android.net.ConnectivityManager
import android.net.IDnsResolver
import android.net.INetd
-import android.net.INetworkStatsService
import android.net.LinkProperties
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
@@ -37,7 +37,6 @@ import android.net.Uri
import android.net.metrics.IpConnectivityLog
import android.os.ConditionVariable
import android.os.IBinder
-import android.os.INetworkManagementService
import android.os.SystemConfigManager
import android.os.UserHandle
import android.testing.TestableContext
@@ -87,9 +86,7 @@ class ConnectivityServiceIntegrationTest {
// lateinit used here for mocks as they need to be reinitialized between each test and the test
// should crash if they are used before being initialized.
@Mock
- private lateinit var netManager: INetworkManagementService
- @Mock
- private lateinit var statsService: INetworkStatsService
+ private lateinit var statsManager: NetworkStatsManager
@Mock
private lateinit var log: IpConnectivityLog
@Mock
@@ -172,12 +169,13 @@ class ConnectivityServiceIntegrationTest {
service = TestConnectivityService(makeDependencies())
cm = ConnectivityManager(context, service)
context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
+ context.addMockSystemService(Context.NETWORK_STATS_SERVICE, statsManager)
service.systemReadyInternal()
}
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
- context, statsService, dnsResolver, log, netd, deps)
+ context, dnsResolver, log, netd, deps)
private fun makeDependencies(): ConnectivityService.Dependencies {
val deps = spy(ConnectivityService.Dependencies())
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4d5cd9af17ef..3bc15a386183 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -149,6 +149,7 @@ import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -180,7 +181,6 @@ import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.INetworkStatsService;
import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IQosCallback;
import android.net.InetAddresses;
@@ -203,7 +203,6 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
-import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
@@ -250,7 +249,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.Credentials;
-import android.security.KeyStore;
import android.system.Os;
import android.telephony.TelephonyManager;
import android.telephony.data.EpsBearerQosSessionAttributes;
@@ -282,6 +280,7 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.testutils.ExceptionUtils;
@@ -423,7 +422,7 @@ public class ConnectivityServiceTest {
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
- @Mock INetworkStatsService mStatsService;
+ @Mock NetworkStatsManager mStatsManager;
@Mock IBatteryStats mBatteryStatsService;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@@ -440,7 +439,7 @@ public class ConnectivityServiceTest {
@Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
- @Mock KeyStore mKeyStore;
+ @Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
@@ -539,6 +538,7 @@ public class ConnectivityServiceTest {
if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
+ if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
return super.getSystemService(name);
}
@@ -1124,7 +1124,7 @@ public class ConnectivityServiceTest {
return mDeviceIdleInternal;
}
},
- mNetworkManagementService, mMockNetd, userId, mKeyStore);
+ mNetworkManagementService, mMockNetd, userId, mVpnProfileStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1303,8 +1303,9 @@ public class ConnectivityServiceTest {
return mVMSHandlerThread;
}
- public KeyStore getKeyStore() {
- return mKeyStore;
+ @Override
+ public VpnProfileStore getVpnProfileStore() {
+ return mVpnProfileStore;
}
public INetd getNetd() {
@@ -1471,7 +1472,6 @@ public class ConnectivityServiceTest {
mDeps = makeDependencies();
returnRealCallingUid();
mService = new ConnectivityService(mServiceContext,
- mStatsService,
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
@@ -5486,18 +5486,19 @@ public class ConnectivityServiceTest {
assertEquals(expectedSet, actualSet);
}
- private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+ private void expectNetworkStatus(Network[] networks, String defaultIface,
Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
- ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
- ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(
- UnderlyingNetworkInfo[].class);
+ ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
+ ArgumentCaptor.forClass(List.class);
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
- any(NetworkStateSnapshot[].class), eq(defaultIface), vpnInfosCaptor.capture());
+ verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
+ any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+ assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks);
- UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue();
+ UnderlyingNetworkInfo[] infos =
+ vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]);
if (vpnUid != null) {
assertEquals("Should have exactly one VPN:", 1, infos.length);
UnderlyingNetworkInfo info = infos[0];
@@ -5511,8 +5512,9 @@ public class ConnectivityServiceTest {
}
}
- private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
- expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+ private void expectNetworkStatus(
+ Network[] networks, String defaultIface) throws Exception {
+ expectNetworkStatus(networks, defaultIface, null, null, new String[0]);
}
@Test
@@ -5532,46 +5534,46 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ reset(mStatsManager);
// Default network switch should update ifaces.
mWiFiNetworkAgent.connect(false);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyWifi, WIFI_IFNAME);
+ reset(mStatsManager);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ reset(mStatsManager);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ reset(mStatsManager);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ reset(mStatsManager);
// Temp metered change shouldn't update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
waitForIdle();
- verify(mStatsService, never()).forceUpdateIfaces(eq(onlyCell), any(
- NetworkStateSnapshot[].class), eq(MOBILE_IFNAME), eq(new UnderlyingNetworkInfo[0]));
- reset(mStatsService);
+ verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
+ any(List.class), eq(MOBILE_IFNAME), any(List.class));
+ reset(mStatsManager);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
- reset(mStatsService);
+ expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+ reset(mStatsManager);
// Test VPNs.
final LinkProperties lp = new LinkProperties();
@@ -5584,7 +5586,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
// A VPN with default (null) underlying networks sets the underlying network's interfaces...
- expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
// ...and updates them as the default network switches.
@@ -5601,9 +5603,9 @@ public class ConnectivityServiceTest {
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
- reset(mStatsService);
+ reset(mStatsManager);
// A VPN that sets its underlying networks passes the underlying interfaces, and influences
// the default interface sent to NetworkStatsService by virtue of applying to the system
@@ -5613,22 +5615,22 @@ public class ConnectivityServiceTest {
// applies to the system server UID should not have any bearing on network stats.
mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
- reset(mStatsService);
+ reset(mStatsManager);
mMockVpn.setUnderlyingNetworks(cellAndWifi);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
- reset(mStatsService);
+ reset(mStatsManager);
// Null underlying networks are ignored.
mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
- reset(mStatsService);
+ reset(mStatsManager);
// If an underlying network disconnects, that interface should no longer be underlying.
// This doesn't actually work because disconnectAndDestroyNetwork only notifies
@@ -5640,17 +5642,17 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
waitForIdle();
assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
- expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
// Confirm that we never tell NetworkStatsService that cell is no longer the underlying
// network for the VPN...
- verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
- any(NetworkStateSnapshot[].class), any() /* anyString() doesn't match null */,
- argThat(infos -> infos[0].underlyingIfaces.size() == 1
- && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0))));
- verifyNoMoreInteractions(mStatsService);
- reset(mStatsService);
+ verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
+ any(List.class), any() /* anyString() doesn't match null */,
+ argThat(infos -> infos.get(0).underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(infos.get(0).underlyingIfaces.get(0))));
+ verifyNoMoreInteractions(mStatsManager);
+ reset(mStatsManager);
// ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
// called again, it does. For example, connect Ethernet, but with a low score, such that it
@@ -5659,13 +5661,13 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.adjustScore(-40);
mEthernetNetworkAgent.connect(false);
waitForIdle();
- verify(mStatsService).forceUpdateIfaces(any(Network[].class),
- any(NetworkStateSnapshot[].class), any() /* anyString() doesn't match null */,
- argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1
- && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0))));
+ verify(mStatsManager).notifyNetworkStatus(any(List.class),
+ any(List.class), any() /* anyString() doesn't match null */,
+ argThat(vpnInfos -> vpnInfos.get(0).underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(vpnInfos.get(0).underlyingIfaces.get(0))));
mEthernetNetworkAgent.disconnect();
waitForIdle();
- reset(mStatsService);
+ reset(mStatsManager);
// When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
// does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
@@ -5675,27 +5677,27 @@ public class ConnectivityServiceTest {
// Also, for the same reason as above, the active interface passed in is null.
mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, null);
- reset(mStatsService);
+ expectNetworkStatus(wifiAndVpn, null);
+ reset(mStatsManager);
// Specifying only a null underlying network is the same as no networks.
mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, null);
- reset(mStatsService);
+ expectNetworkStatus(wifiAndVpn, null);
+ reset(mStatsManager);
// Specifying networks that are all disconnected is the same as specifying no networks.
mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, null);
- reset(mStatsService);
+ expectNetworkStatus(wifiAndVpn, null);
+ reset(mStatsManager);
// Passing in null again means follow the default network again.
mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
- expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
- reset(mStatsService);
+ reset(mStatsManager);
}
@Test
@@ -7509,8 +7511,7 @@ public class ConnectivityServiceTest {
private void setupLegacyLockdownVpn() {
final String profileName = "testVpnProfile";
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
- when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true);
- when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
+ when(mVpnProfileStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
final VpnProfile profile = new VpnProfile(profileName);
profile.name = "My VPN";
@@ -7518,7 +7519,7 @@ public class ConnectivityServiceTest {
profile.dnsServers = "8.8.8.8";
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
final byte[] encodedProfile = profile.encode();
- when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
+ when(mVpnProfileStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
private void establishLegacyLockdownVpn(Network underlying) throws Exception {
@@ -9876,12 +9877,11 @@ public class ConnectivityServiceTest {
.build();
// Act on ConnectivityService.setOemNetworkPreference()
- final TestOemListenerCallback mOnSetOemNetworkPreferenceTestListener =
- new TestOemListenerCallback();
- mService.setOemNetworkPreference(pref, mOnSetOemNetworkPreferenceTestListener);
+ final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+ mService.setOemNetworkPreference(pref, oemPrefListener);
// Verify call returned successfully
- mOnSetOemNetworkPreferenceTestListener.expectOnComplete();
+ oemPrefListener.expectOnComplete();
}
private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 7489a0f889dc..b8f7fbca3983 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -91,7 +91,6 @@ import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.Credentials;
-import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Range;
@@ -196,7 +195,7 @@ public class VpnTest {
@Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
@Mock private IpSecService mIpSecService;
- @Mock private KeyStore mKeyStore;
+ @Mock private VpnProfileStore mVpnProfileStore;
private final VpnProfile mVpnProfile;
private IpSecManager mIpSecManager;
@@ -333,17 +332,17 @@ public class VpnTest {
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList(), mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList(), mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList(), mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -354,17 +353,17 @@ public class VpnTest {
final UidRange user = PRI_USER_RANGE;
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -382,14 +381,14 @@ public class VpnTest {
// Set always-on with lockdown and allow app PKGS[2] from lockdown.
assertTrue(vpn.setAlwaysOnPackage(
- PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
+ PKGS[1], true, Collections.singletonList(PKGS[2])));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
// Change allowed app list to PKGS[3].
assertTrue(vpn.setAlwaysOnPackage(
- PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
+ PKGS[1], true, Collections.singletonList(PKGS[3])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -400,7 +399,7 @@ public class VpnTest {
// Change the VPN app.
assertTrue(vpn.setAlwaysOnPackage(
- PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
+ PKGS[0], true, Collections.singletonList(PKGS[3])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
@@ -411,7 +410,7 @@ public class VpnTest {
}));
// Remove the list of allowed packages.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -422,7 +421,7 @@ public class VpnTest {
// Add the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(
- PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
+ PKGS[0], true, Collections.singletonList(PKGS[1])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
}));
@@ -433,12 +432,12 @@ public class VpnTest {
// Try allowing a package with a comma, should be rejected.
assertFalse(vpn.setAlwaysOnPackage(
- PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore));
+ PKGS[0], true, Collections.singletonList("a.b,c.d")));
// Pass a non-existent packages in the allowlist, they (and only they) should be ignored.
// allowed package should change from PGKS[1] to PKGS[2].
assertTrue(vpn.setAlwaysOnPackage(
- PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
+ PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -525,22 +524,22 @@ public class VpnTest {
.thenReturn(Collections.singletonList(resInfo));
// null package name should return false
- assertFalse(vpn.isAlwaysOnPackageSupported(null, mKeyStore));
+ assertFalse(vpn.isAlwaysOnPackageSupported(null));
// Pre-N apps are not supported
appInfo.targetSdkVersion = VERSION_CODES.M;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
// N+ apps are supported by default
appInfo.targetSdkVersion = VERSION_CODES.N;
- assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
// Apps that opt out explicitly are not supported
appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
Bundle metaData = new Bundle();
metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
svcInfo.metaData = metaData;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
}
@Test
@@ -556,7 +555,7 @@ public class VpnTest {
order.verify(mNotificationManager, atLeastOnce()).cancel(anyString(), anyInt());
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore);
+ vpn.setAlwaysOnPackage(PKGS[0], false, null);
order.verify(mNotificationManager).notify(anyString(), anyInt(), any());
// Stop showing the notification once connected.
@@ -568,7 +567,7 @@ public class VpnTest {
order.verify(mNotificationManager).notify(anyString(), anyInt(), any());
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
+ vpn.setAlwaysOnPackage(null, false, null);
order.verify(mNotificationManager).cancel(anyString(), anyInt());
}
@@ -608,15 +607,13 @@ public class VpnTest {
}
private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, String... checkedOps) {
- assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore));
+ assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile));
// The profile should always be stored, whether or not consent has been previously granted.
- verify(mKeyStore)
+ verify(mVpnProfileStore)
.put(
eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)),
- eq(mVpnProfile.encode()),
- eq(Process.SYSTEM_UID),
- eq(0));
+ eq(mVpnProfile.encode()));
for (final String checkedOpStr : checkedOps) {
verify(mAppOps).noteOpNoThrow(checkedOpStr, Process.myUid(), TEST_VPN_PKG,
@@ -671,7 +668,7 @@ public class VpnTest {
bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]);
try {
- vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile, mKeyStore);
+ vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile);
fail("Expected IAE due to profile size");
} catch (IllegalArgumentException expected) {
}
@@ -684,7 +681,7 @@ public class VpnTest {
restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
try {
- vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore);
+ vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile);
fail("Expected SecurityException due to restricted user");
} catch (SecurityException expected) {
}
@@ -694,10 +691,10 @@ public class VpnTest {
public void testDeleteVpnProfile() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
- vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.deleteVpnProfile(TEST_VPN_PKG);
- verify(mKeyStore)
- .delete(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), eq(Process.SYSTEM_UID));
+ verify(mVpnProfileStore)
+ .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
}
@Test
@@ -707,7 +704,7 @@ public class VpnTest {
restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
try {
- vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.deleteVpnProfile(TEST_VPN_PKG);
fail("Expected SecurityException due to restricted user");
} catch (SecurityException expected) {
}
@@ -717,24 +714,24 @@ public class VpnTest {
public void testGetVpnProfilePrivileged() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
- when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(new VpnProfile("").encode());
- vpn.getVpnProfilePrivileged(TEST_VPN_PKG, mKeyStore);
+ vpn.getVpnProfilePrivileged(TEST_VPN_PKG);
- verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
}
@Test
public void testStartVpnProfile() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
- when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(mVpnProfile.encode());
- vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.startVpnProfile(TEST_VPN_PKG);
- verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
verify(mAppOps)
.noteOpNoThrow(
eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
@@ -748,10 +745,10 @@ public class VpnTest {
public void testStartVpnProfileVpnServicePreconsented() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN);
- when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(mVpnProfile.encode());
- vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.startVpnProfile(TEST_VPN_PKG);
// Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown.
verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(),
@@ -763,7 +760,7 @@ public class VpnTest {
final Vpn vpn = createVpnAndSetupUidChecks();
try {
- vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.startVpnProfile(TEST_VPN_PKG);
fail("Expected failure due to no user consent");
} catch (SecurityException expected) {
}
@@ -780,22 +777,22 @@ public class VpnTest {
TEST_VPN_PKG, null /* attributionTag */, null /* message */);
// Keystore should never have been accessed.
- verify(mKeyStore, never()).get(any());
+ verify(mVpnProfileStore, never()).get(any());
}
@Test
public void testStartVpnProfileMissingProfile() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
- when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
try {
- vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.startVpnProfile(TEST_VPN_PKG);
fail("Expected failure due to missing profile");
} catch (IllegalArgumentException expected) {
}
- verify(mKeyStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ verify(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
verify(mAppOps)
.noteOpNoThrow(
eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
@@ -812,7 +809,7 @@ public class VpnTest {
restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
try {
- vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ vpn.startVpnProfile(TEST_VPN_PKG);
fail("Expected SecurityException due to restricted user");
} catch (SecurityException expected) {
}
@@ -938,9 +935,9 @@ public class VpnTest {
}
private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
- assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null));
- verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
verify(mAppOps).setMode(
eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
eq(AppOpsManager.MODE_ALLOWED));
@@ -963,11 +960,11 @@ public class VpnTest {
final int uid = Process.myUid() + 1;
when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
.thenReturn(uid);
- when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(mVpnProfile.encode());
setAndVerifyAlwaysOnPackage(vpn, uid, false);
- assertTrue(vpn.startAlwaysOnVpn(mKeyStore));
+ assertTrue(vpn.startAlwaysOnVpn());
// TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
// a subsequent CL.
@@ -984,7 +981,7 @@ public class VpnTest {
InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute);
- vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
+ vpn.startLegacyVpn(vpnProfile, EGRESS_NETWORK, lp);
return vpn;
}
@@ -1186,7 +1183,7 @@ public class VpnTest {
.thenReturn(asUserContext);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
- mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+ mNetd, userId, mVpnProfileStore, mSystemServices, mIkev2SessionCreator);
verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
provider -> provider.getName().contains("VpnNetworkProvider")
));
diff --git a/tests/vcn/assets/self-signed-ca.pem b/tests/vcn/assets/self-signed-ca.pem
new file mode 100644
index 000000000000..5135ea7077a8
--- /dev/null
+++ b/tests/vcn/assets/self-signed-ca.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIICrKLpR7LxlowDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE
+BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxHDAaBgNVBAMTE2NhLnRlc3QuYW5kcm9p
+ZC5uZXQwHhcNMTkwNzE2MTcxNTUyWhcNMjkwNzEzMTcxNTUyWjA9MQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHQW5kcm9pZDEcMBoGA1UEAxMTY2EudGVzdC5hbmRyb2lk
+Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANsvTwad2Nie0VOy
+Xb1VtHL0R760Jm4vr14JWMcX4oiE6jUdTNdXQ0CGb65wvulP2aEeukFH0D/cvBMR
+Bv9+haEwo9/grIXg9ALNKp+GfuZYw/dfnUMHFn3g2+SUgP6BoMZc4lkHktjkDKxp
+99Q6h4NP/ip1labkhBeB9+Z6l78LTixKRKspNITWASJed9bjzshYxKHi6dJy3maQ
+1LwYKmK7PEGRpoDoT8yZhFbxsVDUojGnJKH1RLXVOn/psG6dI/+IsbTipAttj5zc
+g2VAD56PZG2Jd+vsup+g4Dy72hyy242x5c/H2LKZn4X0B0B+IXyii/ZVc+DJldQ5
+JqplOL8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
+HQYDVR0OBBYEFGYUzuvZUaVJl8mcxejuFiUNGcTfMA0GCSqGSIb3DQEBCwUAA4IB
+AQDQYeqjvHsK2ZqSqxakDp0nu36Plbj48Wvx1ru7GW2faz7i0w/Zkxh06zniILCb
+QJRjDebSTHc5SSbCFrRTvqagaLDhbH42/hQncWqIoJqW+pmznJET4JiBO0sqzm05
+yQWsLI/h9Ir28Y2g5N+XPBU0VVVejQqH4iI0iwQx7y7ABssQ0Xa/K73VPbeGaKd6
+Prt4wjJvTlIL2yE2+0MggJ3F2rNptL5SDpg3g+4/YQ6wVRBFil95kUqplEsCtU4P
+t+8RghiEmsRx/8CywKfZ5Hex87ODhsSDmDApcefbd5gxoWVkqxZUkPcKwYv1ucm8
+u4r44fj4/9W0Zeooav5Yoh1q
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 66590c92579b..7515971b8307 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -203,9 +203,6 @@ public class VcnManagerTest {
IVcnStatusCallback cbBinder =
new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
- cbBinder.onEnteredSafeMode();
- verify(mMockStatusCallback).onEnteredSafeMode();
-
cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
new file mode 100644
index 000000000000..bc8e9d3200b6
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.eap.EapSessionConfig;
+import android.os.PersistableBundle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EapSessionConfigUtilsTest {
+ private static final byte[] EAP_ID = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final int SUB_ID = 1;
+ private static final String NETWORK_NAME = "android.net";
+ private static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = true;
+
+ private EapSessionConfig.Builder createBuilderWithId() {
+ return new EapSessionConfig.Builder().setEapIdentity(EAP_ID);
+ }
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(EapSessionConfig config) {
+ final PersistableBundle bundle = EapSessionConfigUtils.toPersistableBundle(config);
+ final EapSessionConfig resultConfig = EapSessionConfigUtils.fromPersistableBundle(bundle);
+
+ assertEquals(config, resultConfig);
+ }
+
+ @Test
+ public void testSetEapMsChapV2EncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapSimEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapSimConfig(SUB_ID, APPTYPE_USIM).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapAkaEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId().setEapAkaConfig(SUB_ID, APPTYPE_USIM).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapAkaPrimeEncodeDecodeIsLossless() throws Exception {
+ final EapSessionConfig config =
+ createBuilderWithId()
+ .setEapAkaPrimeConfig(
+ SUB_ID, APPTYPE_USIM, NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES)
+ .build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+
+ @Test
+ public void testSetEapTtlsEncodeDecodeIsLossless() throws Exception {
+ final InputStream inputStream =
+ InstrumentationRegistry.getContext()
+ .getResources()
+ .getAssets()
+ .open("self-signed-ca.pem");
+ final CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ final X509Certificate trustedCa =
+ (X509Certificate) factory.generateCertificate(inputStream);
+
+ final EapSessionConfig innerConfig =
+ new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+ final EapSessionConfig config =
+ new EapSessionConfig.Builder().setEapTtlsConfig(trustedCa, innerConfig).build();
+
+ verifyPersistableBundleEncodeDecodeIsLossless(config);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
new file mode 100644
index 000000000000..4f3930f9b5af
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.security.auth.x500.X500Principal;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IkeIdentificationUtilsTest {
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeIdentification id) {
+ final PersistableBundle bundle = IkeIdentificationUtils.toPersistableBundle(id);
+ final IkeIdentification result = IkeIdentificationUtils.fromPersistableBundle(bundle);
+
+ assertEquals(result, id);
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIpv4AddressId() throws Exception {
+ final Inet4Address ipv4Address = (Inet4Address) InetAddress.getByName("192.0.2.100");
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv4AddrIdentification(ipv4Address));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIpv6AddressId() throws Exception {
+ final Inet6Address ipv6Address = (Inet6Address) InetAddress.getByName("2001:db8:2::100");
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv6AddrIdentification(ipv6Address));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeRfc822AddrId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(new IkeFqdnIdentification("ike.android.net"));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeFqdnId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeRfc822AddrIdentification("androidike@example.com"));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeKeyId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeKeyIdIdentification("androidIkeKeyId".getBytes()));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeDerAsn1DnId() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeDerAsn1DnIdentification(
+ new X500Principal("CN=small.server.test.android.net, O=Android, C=US")));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
new file mode 100644
index 000000000000..8ae8692b4f75
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SaProposalUtilsTest {
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
+ final IkeSaProposal proposal =
+ new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256)
+ .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
+ .build();
+
+ final PersistableBundle bundle = IkeSaProposalUtils.toPersistableBundle(proposal);
+ final SaProposal resultProposal = IkeSaProposalUtils.fromPersistableBundle(bundle);
+
+ assertEquals(proposal, resultProposal);
+ }
+
+ /** Package private so that TunnelModeChildSessionParamsUtilsTest can use it */
+ static ChildSaProposal buildTestChildSaProposal() {
+ return new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_192)
+ .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(SaProposal.DH_GROUP_4096_BIT_MODP)
+ .build();
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessChildProposal() throws Exception {
+ final ChildSaProposal proposal = buildTestChildSaProposal();
+
+ final PersistableBundle bundle = ChildSaProposalUtils.toPersistableBundle(proposal);
+ final SaProposal resultProposal = ChildSaProposalUtils.fromPersistableBundle(bundle);
+
+ assertEquals(proposal, resultProposal);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
new file mode 100644
index 000000000000..b3cd0ab80599
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunnelModeChildSessionParamsUtilsTest {
+ private TunnelModeChildSessionParams.Builder createBuilderMinimum() {
+ final ChildSaProposal saProposal = SaProposalUtilsTest.buildTestChildSaProposal();
+ return new TunnelModeChildSessionParams.Builder().addSaProposal(saProposal);
+ }
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(
+ TunnelModeChildSessionParams params) {
+ final PersistableBundle bundle =
+ TunnelModeChildSessionParamsUtils.toPersistableBundle(params);
+ final TunnelModeChildSessionParams result =
+ TunnelModeChildSessionParamsUtils.fromPersistableBundle(bundle);
+
+ assertEquals(params, result);
+ }
+
+ @Test
+ public void testMinimumParamsEncodeDecodeIsLossless() throws Exception {
+ final TunnelModeChildSessionParams sessionParams = createBuilderMinimum().build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetTsEncodeDecodeIsLossless() throws Exception {
+ final IkeTrafficSelector tsInbound =
+ new IkeTrafficSelector(
+ 16,
+ 65520,
+ InetAddresses.parseNumericAddress("192.0.2.100"),
+ InetAddresses.parseNumericAddress("192.0.2.101"));
+ final IkeTrafficSelector tsOutbound =
+ new IkeTrafficSelector(
+ 32,
+ 256,
+ InetAddresses.parseNumericAddress("192.0.2.200"),
+ InetAddresses.parseNumericAddress("192.0.2.255"));
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum()
+ .addInboundTrafficSelectors(tsInbound)
+ .addOutboundTrafficSelectors(tsOutbound)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetLifetimesEncodeDecodeIsLossless() throws Exception {
+ final int hardLifetime = (int) TimeUnit.HOURS.toSeconds(3L);
+ final int softLifetime = (int) TimeUnit.HOURS.toSeconds(1L);
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum().setLifetimeSeconds(hardLifetime, softLifetime).build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+
+ @Test
+ public void testSetConfigRequestsEncodeDecodeIsLossless() throws Exception {
+ final int ipv6PrefixLen = 64;
+ final Inet4Address ipv4Address =
+ (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
+ final Inet6Address ipv6Address =
+ (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
+
+ final TunnelModeChildSessionParams sessionParams =
+ createBuilderMinimum()
+ .addInternalAddressRequest(AF_INET)
+ .addInternalAddressRequest(AF_INET6)
+ .addInternalAddressRequest(ipv4Address)
+ .addInternalAddressRequest(ipv6Address, ipv6PrefixLen)
+ .addInternalDnsServerRequest(AF_INET)
+ .addInternalDnsServerRequest(AF_INET6)
+ .addInternalDhcpServerRequest(AF_INET)
+ .build();
+ verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9b500a7271d7..73a6b88e29ed 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -100,6 +100,8 @@ import java.util.UUID;
public class VcnManagementServiceTest {
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
+ private static final String TEST_CB_PACKAGE_NAME =
+ VcnManagementServiceTest.class.getPackage().getName() + ".callback";
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
private static final VcnConfig TEST_VCN_CONFIG;
@@ -288,6 +290,14 @@ public class VcnManagementServiceTest {
private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
+ return triggerSubscriptionTrackerCbAndGetSnapshot(
+ activeSubscriptionGroups, subIdToGroupMap, true /* hasCarrierPrivileges */);
+ }
+
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups,
+ Map<Integer, ParcelUuid> subIdToGroupMap,
+ boolean hasCarrierPrivileges) {
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
@@ -295,7 +305,7 @@ public class VcnManagementServiceTest {
(activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty())
? Collections.emptySet()
: Collections.singleton(TEST_PACKAGE_NAME);
- doReturn(true)
+ doReturn(hasCarrierPrivileges)
.when(snapshot)
.packageHasPermissionsForSubscriptionGroup(
argThat(val -> activeSubscriptionGroups.contains(val)),
@@ -549,13 +559,6 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
- private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
- mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
-
- triggerSubscriptionTrackerCbAndGetSnapshot(
- Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
- }
-
private void verifyMergedNetworkCapabilities(
NetworkCapabilities mergedCapabilities,
@Transport int transportType,
@@ -573,9 +576,23 @@ public class VcnManagementServiceTest {
}
private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) {
- setUpVcnSubscription(subId, subGrp);
+ setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive, true /* hasCarrierPrivileges */);
+ }
+
+ private void setupSubscriptionAndStartVcn(
+ int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
+ mVcnMgmtSvc.systemReady();
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(subGrp),
+ Collections.singletonMap(subId, subGrp),
+ hasCarrierPrivileges);
+
final Vcn vcn = startAndGetVcnInstance(subGrp);
doReturn(isVcnActive).when(vcn).isActive();
+
+ doReturn(true)
+ .when(mLocationPermissionChecker)
+ .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
}
private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
@@ -721,7 +738,7 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
- private void verifyVcnCallback(
+ private void triggerVcnSafeMode(
@NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
throws Exception {
verify(mMockDeps)
@@ -732,20 +749,20 @@ public class VcnManagementServiceTest {
eq(snapshot),
mVcnCallbackCaptor.capture());
- mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
-
VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
vcnCallback.onEnteredSafeMode();
-
- verify(mMockPolicyListener).onPolicyChanged();
}
@Test
- public void testVcnCallbackOnEnteredSafeMode() throws Exception {
+ public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
- verifyVcnCallback(TEST_UUID_1, snapshot);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ triggerVcnSafeMode(TEST_UUID_1, snapshot);
+
+ verify(mMockPolicyListener).onPolicyChanged();
}
private void triggerVcnStatusCallbackOnEnteredSafeMode(
@@ -758,6 +775,9 @@ public class VcnManagementServiceTest {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));
+ setupSubscriptionAndStartVcn(
+ TEST_SUBSCRIPTION_ID, subGroup, true /* isActive */, hasPermissionsforSubGroup);
+
doReturn(hasPermissionsforSubGroup)
.when(snapshot)
.packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
@@ -768,10 +788,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
- // Trigger systemReady() to set up LocationPermissionChecker
- mVcnMgmtSvc.systemReady();
-
- verifyVcnCallback(subGroup, snapshot);
+ triggerVcnSafeMode(subGroup, snapshot);
}
@Test
@@ -825,6 +842,83 @@ public class VcnManagementServiceTest {
assertEquals(TEST_PACKAGE_NAME, cbInfo.mPkgName);
assertEquals(TEST_UID, cbInfo.mUid);
verify(mMockIBinder).linkToDeath(eq(cbInfo), anyInt());
+
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
+ }
+
+ @Test
+ public void testRegisterVcnStatusCallback_MissingPermission() throws Exception {
+ setupSubscriptionAndStartVcn(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_1,
+ true /* isActive */,
+ false /* hasCarrierPrivileges */);
+
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
+ }
+
+ @Test
+ public void testRegisterVcnStatusCallback_VcnInactive() throws Exception {
+ setupSubscriptionAndStartVcn(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_1,
+ true /* isActive */,
+ true /* hasCarrierPrivileges */);
+
+ // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
+ // timeout so the VCN goes inactive.
+ final TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(TEST_UUID_1),
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
+ false /* hasCarrierPrivileges */);
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
+ // when the status callback is registered). Instead, setup permissions for TEST_CB_PACKAGE
+ // so that it's permissioned to receive INACTIVE (instead of NOT_CONFIGURED) without
+ // reactivating the VCN.
+ doReturn(true)
+ .when(snapshot)
+ .packageHasPermissionsForSubscriptionGroup(
+ eq(TEST_UUID_1), eq(TEST_CB_PACKAGE_NAME));
+ doReturn(true)
+ .when(mLocationPermissionChecker)
+ .checkLocationPermission(eq(TEST_CB_PACKAGE_NAME), any(), eq(TEST_UID), any());
+
+ mVcnMgmtSvc.registerVcnStatusCallback(
+ TEST_UUID_1, mMockStatusCallback, TEST_CB_PACKAGE_NAME);
+
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_INACTIVE);
+ }
+
+ @Test
+ public void testRegisterVcnStatusCallback_VcnActive() throws Exception {
+ setupSubscriptionAndStartVcn(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_1,
+ true /* isActive */,
+ true /* hasCarrierPrivileges */);
+
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
+ }
+
+ @Test
+ public void testRegisterVcnStatusCallback_VcnSafeMode() throws Exception {
+ setupSubscriptionAndStartVcn(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_1,
+ false /* isActive */,
+ true /* hasCarrierPrivileges */);
+
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+ verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
}
@Test(expected = IllegalStateException.class)
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
index df64a801e213..2a88732b50b1 100644
--- a/tools/bit/make.cpp
+++ b/tools/bit/make.cpp
@@ -89,8 +89,8 @@ BuildVars::BuildVars(const string& outDir, const string& buildProduct,
}
Json::Value json;
- Json::Reader reader;
- if (!reader.parse(stream, json)) {
+ Json::CharReaderBuilder builder;
+ if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
return;
}
@@ -132,8 +132,9 @@ BuildVars::save()
return;
}
- Json::StyledStreamWriter writer(" ");
-
+ Json::StreamWriterBuilder factory;
+ factory["indentation"] = " ";
+ std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
Json::Value json(Json::objectValue);
for (map<string,string>::const_iterator it = m_cache.begin(); it != m_cache.end(); it++) {
@@ -141,7 +142,7 @@ BuildVars::save()
}
std::ofstream stream(m_filename, std::ofstream::binary);
- writer.write(stream, json);
+ writer->write(json, &stream);
}
string
@@ -212,8 +213,8 @@ read_modules(const string& buildOut, const string& device, map<string,Module>* r
}
Json::Value json;
- Json::Reader reader;
- if (!reader.parse(stream, json)) {
+ Json::CharReaderBuilder builder;
+ if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
json_error(filename, "can't parse json format", quiet);
return;
}